Update pybind11 to 2.9.2

Fixes https://gitlab.com/kicad/code/kicad/-/issues/10729

(cherry picked from commit 8d003804e0)
This commit is contained in:
Seth Hillbrand 2022-06-20 15:59:56 -07:00
parent e0802dcd26
commit 8ce38d3820
169 changed files with 13624 additions and 6672 deletions

View File

@ -7,13 +7,18 @@
cmake_minimum_required(VERSION 3.4) cmake_minimum_required(VERSION 3.4)
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with # The `cmake_minimum_required(VERSION 3.4...3.22)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate # some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround: # the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.18) if(${CMAKE_VERSION} VERSION_LESS 3.22)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else() else()
cmake_policy(VERSION 3.18) cmake_policy(VERSION 3.22)
endif()
# Avoid infinite recursion if tests include this as a subdirectory
if(DEFINED PYBIND11_MASTER_PROJECT)
return()
endif() endif()
# Extract project version from source # Extract project version from source
@ -45,13 +50,8 @@ if(NOT pybind11_FIND_QUIETLY)
message(STATUS "pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}") message(STATUS "pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}")
endif() endif()
# Avoid infinite recursion if tests include this as a subdirectory
if(DEFINED PYBIND11_MASTER_PROJECT)
set(PYBIND11_TEST OFF)
endif()
# Check if pybind11 is being used directly or via add_subdirectory # Check if pybind11 is being used directly or via add_subdirectory
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR AND NOT DEFINED PYBIND11_MASTER_PROJECT) if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
### Warn if not an out-of-source builds ### Warn if not an out-of-source builds
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
set(lines set(lines
@ -80,6 +80,8 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR AND NOT DEFINED PYBIND11_MASTER_
endif() endif()
set(pybind11_system "") set(pybind11_system "")
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
else() else()
set(PYBIND11_MASTER_PROJECT OFF) set(PYBIND11_MASTER_PROJECT OFF)
set(pybind11_system SYSTEM) set(pybind11_system SYSTEM)
@ -89,6 +91,9 @@ endif()
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_NOPYTHON "Disable search for Python" OFF) option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
set(PYBIND11_INTERNALS_VERSION
""
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
cmake_dependent_option( cmake_dependent_option(
USE_PYTHON_INCLUDE_DIR USE_PYTHON_INCLUDE_DIR
@ -183,6 +188,10 @@ if(NOT TARGET pybind11_headers)
target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals
cxx_right_angle_brackets) cxx_right_angle_brackets)
if(NOT "${PYBIND11_INTERNALS_VERSION}" STREQUAL "")
target_compile_definitions(
pybind11_headers INTERFACE "PYBIND11_INTERNALS_VERSION=${PYBIND11_INTERNALS_VERSION}")
endif()
else() else()
# It is invalid to install a target twice, too. # It is invalid to install a target twice, too.
set(PYBIND11_INSTALL OFF) set(PYBIND11_INSTALL OFF)

View File

@ -3,7 +3,7 @@
**pybind11 — Seamless operability between C++11 and Python** **pybind11 — Seamless operability between C++11 and Python**
|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |CI| |Build status| |Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |GitHub Discussions| |CI| |Build status|
|Repology| |PyPI package| |Conda-forge| |Python Versions| |Repology| |PyPI package| |Conda-forge| |Python Versions|
@ -13,19 +13,6 @@
.. start .. start
.. warning::
Combining older versions of pybind11 (< 2.6.0) with Python 3.9.0 will
trigger undefined behavior that typically manifests as crashes during
interpreter shutdown (but could also destroy your data. **You have been
warned.**)
We recommend that you update to the latest patch release of Python (3.9.1),
which includes a `fix <https://github.com/python/cpython/pull/22670>`_
that resolves this problem. If you do use Python 3.9.0, please update to
the latest version of pybind11 (2.6.0 or newer), which includes a temporary
workaround specifically when Python 3.9.0 is detected at runtime.
**pybind11** is a lightweight header-only library that exposes C++ types **pybind11** is a lightweight header-only library that exposes C++ types
in Python and vice versa, mainly to create Python bindings of existing in Python and vice versa, mainly to create Python bindings of existing
@ -110,7 +97,7 @@ goodies:
transparently applied to all entries of one or more NumPy array transparently applied to all entries of one or more NumPy array
arguments. arguments.
- Pythons slice-based access and assignment operations can be - Python's slice-based access and assignment operations can be
supported with just a few lines of code. supported with just a few lines of code.
- Everything is contained in just a few header files; there is no need - Everything is contained in just a few header files; there is no need
@ -119,7 +106,7 @@ goodies:
- Binaries are generally smaller by a factor of at least 2 compared to - Binaries are generally smaller by a factor of at least 2 compared to
equivalent bindings generated by Boost.Python. A recent pybind11 equivalent bindings generated by Boost.Python. A recent pybind11
conversion of PyRosetta, an enormous Boost.Python binding project, conversion of PyRosetta, an enormous Boost.Python binding project,
`reported <http://graylab.jhu.edu/RosettaCon2016/PyRosetta-4.pdf>`_ `reported <https://graylab.jhu.edu/Sergey/2016.RosettaCon/PyRosetta-4.pdf>`_
a binary size reduction of **5.4x** and compile time reduction by a binary size reduction of **5.4x** and compile time reduction by
**5.8x**. **5.8x**.
@ -147,9 +134,9 @@ About
This project was created by `Wenzel This project was created by `Wenzel
Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or
improvements to the code were contributed by Jonas Adler, Lori A. Burns, improvements to the code were contributed by Jonas Adler, Lori A. Burns,
Sylvain Corlay, Eric Cousineau, Ralf Grosse-Kunstleve, Trent Houliston, Axel Sylvain Corlay, Eric Cousineau, Aaron Gokaslan, Ralf Grosse-Kunstleve, Trent Houliston, Axel
Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov Johan Mabille, Tomasz Miąsko, Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov Johan Mabille, Tomasz Miąsko,
Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim
Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart. Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart.
We thank Google for a generous financial contribution to the continuous We thank Google for a generous financial contribution to the continuous
@ -189,3 +176,5 @@ to the terms and conditions of this license.
:target: https://repology.org/project/python:pybind11/versions :target: https://repology.org/project/python:pybind11/versions
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg .. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg
:target: https://pypi.org/project/pybind11/ :target: https://pypi.org/project/pybind11/
.. |GitHub Discussions| image:: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github
:target: https://github.com/pybind/pybind11/discussions

View File

@ -18,6 +18,5 @@ ALIASES += "endrst=\endverbatim"
QUIET = YES QUIET = YES
WARNINGS = YES WARNINGS = YES
WARN_IF_UNDOCUMENTED = NO WARN_IF_UNDOCUMENTED = NO
PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ PREDEFINED = PY_MAJOR_VERSION=3 \
PY_MAJOR_VERSION=3 \
PYBIND11_NOINLINE PYBIND11_NOINLINE

View File

@ -26,7 +26,9 @@ The following Python snippet demonstrates the intended usage from the Python sid
def __int__(self): def __int__(self):
return 123 return 123
from example import print from example import print
print(A()) print(A())
To register the necessary conversion routines, it is necessary to add an To register the necessary conversion routines, it is necessary to add an
@ -44,7 +46,7 @@ type is explicitly allowed.
* function signatures and declares a local variable * function signatures and declares a local variable
* 'value' of type inty * 'value' of type inty
*/ */
PYBIND11_TYPE_CASTER(inty, _("inty")); PYBIND11_TYPE_CASTER(inty, const_name("inty"));
/** /**
* Conversion part 1 (Python->C++): convert a PyObject into a inty * Conversion part 1 (Python->C++): convert a PyObject into a inty

View File

@ -52,7 +52,7 @@ can be mapped *and* if the numpy array is writeable (that is
the passed variable will be transparently carried out directly on the the passed variable will be transparently carried out directly on the
``numpy.ndarray``. ``numpy.ndarray``.
This means you can can write code such as the following and have it work as This means you can write code such as the following and have it work as
expected: expected:
.. code-block:: cpp .. code-block:: cpp
@ -112,7 +112,7 @@ example:
.. code-block:: python .. code-block:: python
a = MyClass() a = MyClass()
m = a.get_matrix() # flags.writeable = True, flags.owndata = False m = a.get_matrix() # flags.writeable = True, flags.owndata = False
v = a.view_matrix() # flags.writeable = False, flags.owndata = False v = a.view_matrix() # flags.writeable = False, flags.owndata = False
c = a.copy_matrix() # flags.writeable = True, flags.owndata = True c = a.copy_matrix() # flags.writeable = True, flags.owndata = True
# m[5,6] and v[5,6] refer to the same element, c[5,6] does not. # m[5,6] and v[5,6] refer to the same element, c[5,6] does not.
@ -203,7 +203,7 @@ adding the ``order='F'`` option when creating an array:
.. code-block:: python .. code-block:: python
myarray = np.array(source, order='F') myarray = np.array(source, order="F")
Such an object will be passable to a bound function accepting an Such an object will be passable to a bound function accepting an
``Eigen::Ref<MatrixXd>`` (or similar column-major Eigen type). ``Eigen::Ref<MatrixXd>`` (or similar column-major Eigen type).

View File

@ -75,96 +75,96 @@ The following basic data types are supported out of the box (some may require
an additional extension header to be included). To pass other data structures an additional extension header to be included). To pass other data structures
as arguments and return values, refer to the section on binding :ref:`classes`. as arguments and return values, refer to the section on binding :ref:`classes`.
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| Data type | Description | Header file | | Data type | Description | Header file |
+====================================+===========================+===============================+ +====================================+===========================+===================================+
| ``int8_t``, ``uint8_t`` | 8-bit integers | :file:`pybind11/pybind11.h` | | ``int8_t``, ``uint8_t`` | 8-bit integers | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``int16_t``, ``uint16_t`` | 16-bit integers | :file:`pybind11/pybind11.h` | | ``int16_t``, ``uint16_t`` | 16-bit integers | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``int32_t``, ``uint32_t`` | 32-bit integers | :file:`pybind11/pybind11.h` | | ``int32_t``, ``uint32_t`` | 32-bit integers | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``int64_t``, ``uint64_t`` | 64-bit integers | :file:`pybind11/pybind11.h` | | ``int64_t``, ``uint64_t`` | 64-bit integers | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``ssize_t``, ``size_t`` | Platform-dependent size | :file:`pybind11/pybind11.h` | | ``ssize_t``, ``size_t`` | Platform-dependent size | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``float``, ``double`` | Floating point types | :file:`pybind11/pybind11.h` | | ``float``, ``double`` | Floating point types | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``bool`` | Two-state Boolean type | :file:`pybind11/pybind11.h` | | ``bool`` | Two-state Boolean type | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``char`` | Character literal | :file:`pybind11/pybind11.h` | | ``char`` | Character literal | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``char16_t`` | UTF-16 character literal | :file:`pybind11/pybind11.h` | | ``char16_t`` | UTF-16 character literal | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``char32_t`` | UTF-32 character literal | :file:`pybind11/pybind11.h` | | ``char32_t`` | UTF-32 character literal | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``wchar_t`` | Wide character literal | :file:`pybind11/pybind11.h` | | ``wchar_t`` | Wide character literal | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``const char *`` | UTF-8 string literal | :file:`pybind11/pybind11.h` | | ``const char *`` | UTF-8 string literal | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``const char16_t *`` | UTF-16 string literal | :file:`pybind11/pybind11.h` | | ``const char16_t *`` | UTF-16 string literal | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``const char32_t *`` | UTF-32 string literal | :file:`pybind11/pybind11.h` | | ``const char32_t *`` | UTF-32 string literal | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``const wchar_t *`` | Wide string literal | :file:`pybind11/pybind11.h` | | ``const wchar_t *`` | Wide string literal | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::string`` | STL dynamic UTF-8 string | :file:`pybind11/pybind11.h` | | ``std::string`` | STL dynamic UTF-8 string | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::u16string`` | STL dynamic UTF-16 string | :file:`pybind11/pybind11.h` | | ``std::u16string`` | STL dynamic UTF-16 string | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::u32string`` | STL dynamic UTF-32 string | :file:`pybind11/pybind11.h` | | ``std::u32string`` | STL dynamic UTF-32 string | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` | | ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` | | ``std::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` |
| ``std::u16string_view``, etc. | | | | ``std::u16string_view``, etc. | | |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::pair<T1, T2>`` | Pair of two custom types | :file:`pybind11/pybind11.h` | | ``std::pair<T1, T2>`` | Pair of two custom types | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` | | ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::reference_wrapper<...>`` | Reference type wrapper | :file:`pybind11/pybind11.h` | | ``std::reference_wrapper<...>`` | Reference type wrapper | :file:`pybind11/pybind11.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::complex<T>`` | Complex numbers | :file:`pybind11/complex.h` | | ``std::complex<T>`` | Complex numbers | :file:`pybind11/complex.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::array<T, Size>`` | STL static array | :file:`pybind11/stl.h` | | ``std::array<T, Size>`` | STL static array | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::vector<T>`` | STL dynamic array | :file:`pybind11/stl.h` | | ``std::vector<T>`` | STL dynamic array | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::deque<T>`` | STL double-ended queue | :file:`pybind11/stl.h` | | ``std::deque<T>`` | STL double-ended queue | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::valarray<T>`` | STL value array | :file:`pybind11/stl.h` | | ``std::valarray<T>`` | STL value array | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::list<T>`` | STL linked list | :file:`pybind11/stl.h` | | ``std::list<T>`` | STL linked list | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::map<T1, T2>`` | STL ordered map | :file:`pybind11/stl.h` | | ``std::map<T1, T2>`` | STL ordered map | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::unordered_map<T1, T2>`` | STL unordered map | :file:`pybind11/stl.h` | | ``std::unordered_map<T1, T2>`` | STL unordered map | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::set<T>`` | STL ordered set | :file:`pybind11/stl.h` | | ``std::set<T>`` | STL ordered set | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::unordered_set<T>`` | STL unordered set | :file:`pybind11/stl.h` | | ``std::unordered_set<T>`` | STL unordered set | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::optional<T>`` | STL optional type (C++17) | :file:`pybind11/stl.h` | | ``std::optional<T>`` | STL optional type (C++17) | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::experimental::optional<T>`` | STL optional type (exp.) | :file:`pybind11/stl.h` | | ``std::experimental::optional<T>`` | STL optional type (exp.) | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` | | ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::filesystem::path<T>`` | STL path (C++17) [#]_ | :file:`pybind11/stl.h` | | ``std::filesystem::path<T>`` | STL path (C++17) [#]_ | :file:`pybind11/stl/filesystem.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` | | ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` | | ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``std::chrono::time_point<...>`` | STL date/time | :file:`pybind11/chrono.h` | | ``std::chrono::time_point<...>`` | STL date/time | :file:`pybind11/chrono.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``Eigen::Matrix<...>`` | Eigen: dense matrix | :file:`pybind11/eigen.h` | | ``Eigen::Matrix<...>`` | Eigen: dense matrix | :file:`pybind11/eigen.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``Eigen::Map<...>`` | Eigen: mapped memory | :file:`pybind11/eigen.h` | | ``Eigen::Map<...>`` | Eigen: mapped memory | :file:`pybind11/eigen.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
| ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` | | ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` |
+------------------------------------+---------------------------+-------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
.. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and .. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and
``os.PathLike`` is converted to ``std::filesystem::path``, but this requires ``os.PathLike`` is converted to ``std::filesystem::path``, but this requires

View File

@ -36,13 +36,13 @@ everywhere <http://utf8everywhere.org/>`_.
} }
); );
.. code-block:: python .. code-block:: pycon
>>> utf8_test('🎂') >>> utf8_test("🎂")
utf-8 is icing on the cake. utf-8 is icing on the cake.
🎂 🎂
>>> utf8_charptr('🍕') >>> utf8_charptr("🍕")
My favorite food is My favorite food is
🍕 🍕
@ -80,7 +80,7 @@ raise a ``UnicodeDecodeError``.
} }
); );
.. code-block:: python .. code-block:: pycon
>>> isinstance(example.std_string_return(), str) >>> isinstance(example.std_string_return(), str)
True True
@ -114,7 +114,7 @@ conversion has the same overhead as implicit conversion.
} }
); );
.. code-block:: python .. code-block:: pycon
>>> str_output() >>> str_output()
'Send your résumé to Alice in HR' 'Send your résumé to Alice in HR'
@ -143,7 +143,7 @@ returned to Python as ``bytes``, then one can return the data as a
} }
); );
.. code-block:: python .. code-block:: pycon
>>> example.return_bytes() >>> example.return_bytes()
b'\xba\xd0\xba\xd0' b'\xba\xd0\xba\xd0'
@ -160,7 +160,7 @@ encoding, but cannot convert ``std::string`` back to ``bytes`` implicitly.
} }
); );
.. code-block:: python .. code-block:: pycon
>>> isinstance(example.asymmetry(b"have some bytes"), str) >>> isinstance(example.asymmetry(b"have some bytes"), str)
True True
@ -229,16 +229,16 @@ character.
m.def("pass_char", [](char c) { return c; }); m.def("pass_char", [](char c) { return c; });
m.def("pass_wchar", [](wchar_t w) { return w; }); m.def("pass_wchar", [](wchar_t w) { return w; });
.. code-block:: python .. code-block:: pycon
>>> example.pass_char('A') >>> example.pass_char("A")
'A' 'A'
While C++ will cast integers to character types (``char c = 0x65;``), pybind11 While C++ will cast integers to character types (``char c = 0x65;``), pybind11
does not convert Python integers to characters implicitly. The Python function does not convert Python integers to characters implicitly. The Python function
``chr()`` can be used to convert integers to characters. ``chr()`` can be used to convert integers to characters.
.. code-block:: python .. code-block:: pycon
>>> example.pass_char(0x65) >>> example.pass_char(0x65)
TypeError TypeError
@ -259,17 +259,17 @@ a combining acute accent). The combining character will be lost if the
two-character sequence is passed as an argument, even though it renders as a two-character sequence is passed as an argument, even though it renders as a
single grapheme. single grapheme.
.. code-block:: python .. code-block:: pycon
>>> example.pass_wchar('é') >>> example.pass_wchar("é")
'é' 'é'
>>> combining_e_acute = 'e' + '\u0301' >>> combining_e_acute = "e" + "\u0301"
>>> combining_e_acute >>> combining_e_acute
'é' 'é'
>>> combining_e_acute == 'é' >>> combining_e_acute == "é"
False False
>>> example.pass_wchar(combining_e_acute) >>> example.pass_wchar(combining_e_acute)
@ -278,9 +278,9 @@ single grapheme.
Normalizing combining characters before passing the character literal to C++ Normalizing combining characters before passing the character literal to C++
may resolve *some* of these issues: may resolve *some* of these issues:
.. code-block:: python .. code-block:: pycon
>>> example.pass_wchar(unicodedata.normalize('NFC', combining_e_acute)) >>> example.pass_wchar(unicodedata.normalize("NFC", combining_e_acute))
'é' 'é'
In some languages (Thai for example), there are `graphemes that cannot be In some languages (Thai for example), there are `graphemes that cannot be

View File

@ -9,7 +9,7 @@ that you are already familiar with the basics from :doc:`/classes`.
Overriding virtual functions in Python Overriding virtual functions in Python
====================================== ======================================
Suppose that a C++ class or interface has a virtual function that we'd like to Suppose that a C++ class or interface has a virtual function that we'd like
to override from within Python (we'll focus on the class ``Animal``; ``Dog`` is to override from within Python (we'll focus on the class ``Animal``; ``Dog`` is
given as a specific example of how one would do this with traditional C++ given as a specific example of how one would do this with traditional C++
code). code).
@ -136,7 +136,7 @@ a virtual method call.
u'woof! woof! woof! ' u'woof! woof! woof! '
>>> class Cat(Animal): >>> class Cat(Animal):
... def go(self, n_times): ... def go(self, n_times):
... return "meow! " * n_times ... return "meow! " * n_times
... ...
>>> c = Cat() >>> c = Cat()
>>> call_go(c) >>> call_go(c)
@ -159,8 +159,9 @@ Here is an example:
class Dachshund(Dog): class Dachshund(Dog):
def __init__(self, name): def __init__(self, name):
Dog.__init__(self) # Without this, a TypeError is raised. Dog.__init__(self) # Without this, a TypeError is raised.
self.name = name self.name = name
def bark(self): def bark(self):
return "yap!" return "yap!"
@ -1153,12 +1154,65 @@ error:
>>> class PyFinalChild(IsFinal): >>> class PyFinalChild(IsFinal):
... pass ... pass
...
TypeError: type 'IsFinal' is not an acceptable base type TypeError: type 'IsFinal' is not an acceptable base type
.. note:: This attribute is currently ignored on PyPy .. note:: This attribute is currently ignored on PyPy
.. versionadded:: 2.6 .. versionadded:: 2.6
Binding classes with template parameters
========================================
pybind11 can also wrap classes that have template parameters. Consider these classes:
.. code-block:: cpp
struct Cat {};
struct Dog {};
template <typename PetType>
struct Cage {
Cage(PetType& pet);
PetType& get();
};
C++ templates may only be instantiated at compile time, so pybind11 can only
wrap instantiated templated classes. You cannot wrap a non-instantiated template:
.. code-block:: cpp
// BROKEN (this will not compile)
py::class_<Cage>(m, "Cage");
.def("get", &Cage::get);
You must explicitly specify each template/type combination that you want to
wrap separately.
.. code-block:: cpp
// ok
py::class_<Cage<Cat>>(m, "CatCage")
.def("get", &Cage<Cat>::get);
// ok
py::class_<Cage<Dog>>(m, "DogCage")
.def("get", &Cage<Dog>::get);
If your class methods have template parameters you can wrap those as well,
but once again each instantiation must be explicitly specified:
.. code-block:: cpp
typename <typename T>
struct MyClass {
template <typename V>
T fn(V v);
};
py::class<MyClass<int>>(m, "MyClassT")
.def("fn", &MyClass<int>::fn<std::string>);
Custom automatic downcasters Custom automatic downcasters
============================ ============================
@ -1247,7 +1301,7 @@ Accessing the type object
You can get the type object from a C++ class that has already been registered using: You can get the type object from a C++ class that has already been registered using:
.. code-block:: python .. code-block:: cpp
py::type T_py = py::type::of<T>(); py::type T_py = py::type::of<T>();
@ -1259,3 +1313,37 @@ object, just like ``type(ob)`` in Python.
Other types, like ``py::type::of<int>()``, do not work, see :ref:`type-conversions`. Other types, like ``py::type::of<int>()``, do not work, see :ref:`type-conversions`.
.. versionadded:: 2.6 .. versionadded:: 2.6
Custom type setup
=================
For advanced use cases, such as enabling garbage collection support, you may
wish to directly manipulate the ``PyHeapTypeObject`` corresponding to a
``py::class_`` definition.
You can do that using ``py::custom_type_setup``:
.. code-block:: cpp
struct OwnsPythonObjects {
py::object value = py::none();
};
py::class_<OwnsPythonObjects> cls(
m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) {
auto *type = &heap_type->ht_type;
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
Py_VISIT(self.value.ptr());
return 0;
};
type->tp_clear = [](PyObject *self_base) {
auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
self.value = py::none();
return 0;
};
}));
cls.def(py::init<>());
cls.def_readwrite("value", &OwnsPythonObjects::value);
.. versionadded:: 2.8

View File

@ -40,15 +40,15 @@ The essential structure of the ``main.cpp`` file looks like this:
} }
The interpreter must be initialized before using any Python API, which includes The interpreter must be initialized before using any Python API, which includes
all the functions and classes in pybind11. The RAII guard class `scoped_interpreter` all the functions and classes in pybind11. The RAII guard class ``scoped_interpreter``
takes care of the interpreter lifetime. After the guard is destroyed, the interpreter takes care of the interpreter lifetime. After the guard is destroyed, the interpreter
shuts down and clears its memory. No Python functions can be called after this. shuts down and clears its memory. No Python functions can be called after this.
Executing Python code Executing Python code
===================== =====================
There are a few different ways to run Python code. One option is to use `eval`, There are a few different ways to run Python code. One option is to use ``eval``,
`exec` or `eval_file`, as explained in :ref:`eval`. Here is a quick example in ``exec`` or ``eval_file``, as explained in :ref:`eval`. Here is a quick example in
the context of an executable with an embedded interpreter: the context of an executable with an embedded interpreter:
.. code-block:: cpp .. code-block:: cpp
@ -108,7 +108,7 @@ The two approaches can also be combined:
Importing modules Importing modules
================= =================
Python modules can be imported using `module_::import()`: Python modules can be imported using ``module_::import()``:
.. code-block:: cpp .. code-block:: cpp
@ -122,6 +122,7 @@ embedding the interpreter. This makes it easy to import local Python files:
"""calc.py located in the working directory""" """calc.py located in the working directory"""
def add(i, j): def add(i, j):
return i + j return i + j
@ -133,7 +134,7 @@ embedding the interpreter. This makes it easy to import local Python files:
int n = result.cast<int>(); int n = result.cast<int>();
assert(n == 3); assert(n == 3);
Modules can be reloaded using `module_::reload()` if the source is modified e.g. Modules can be reloaded using ``module_::reload()`` if the source is modified e.g.
by an external process. This can be useful in scenarios where the application by an external process. This can be useful in scenarios where the application
imports a user defined data processing script which needs to be updated after imports a user defined data processing script which needs to be updated after
changes by the user. Note that this function does not reload modules recursively. changes by the user. Note that this function does not reload modules recursively.
@ -143,7 +144,7 @@ changes by the user. Note that this function does not reload modules recursively
Adding embedded modules Adding embedded modules
======================= =======================
Embedded binary modules can be added using the `PYBIND11_EMBEDDED_MODULE` macro. Embedded binary modules can be added using the ``PYBIND11_EMBEDDED_MODULE`` macro.
Note that the definition must be placed at global scope. They can be imported Note that the definition must be placed at global scope. They can be imported
like any other module. like any other module.
@ -169,7 +170,7 @@ like any other module.
Unlike extension modules where only a single binary module can be created, on Unlike extension modules where only a single binary module can be created, on
the embedded side an unlimited number of modules can be added using multiple the embedded side an unlimited number of modules can be added using multiple
`PYBIND11_EMBEDDED_MODULE` definitions (as long as they have unique names). ``PYBIND11_EMBEDDED_MODULE`` definitions (as long as they have unique names).
These modules are added to Python's list of builtins, so they can also be These modules are added to Python's list of builtins, so they can also be
imported in pure Python files loaded by the interpreter. Everything interacts imported in pure Python files loaded by the interpreter. Everything interacts
@ -215,9 +216,9 @@ naturally:
Interpreter lifetime Interpreter lifetime
==================== ====================
The Python interpreter shuts down when `scoped_interpreter` is destroyed. After The Python interpreter shuts down when ``scoped_interpreter`` is destroyed. After
this, creating a new instance will restart the interpreter. Alternatively, the this, creating a new instance will restart the interpreter. Alternatively, the
`initialize_interpreter` / `finalize_interpreter` pair of functions can be used ``initialize_interpreter`` / ``finalize_interpreter`` pair of functions can be used
to directly set the state at any time. to directly set the state at any time.
Modules created with pybind11 can be safely re-initialized after the interpreter Modules created with pybind11 can be safely re-initialized after the interpreter
@ -229,8 +230,8 @@ global data. All the details can be found in the CPython documentation.
.. warning:: .. warning::
Creating two concurrent `scoped_interpreter` guards is a fatal error. So is Creating two concurrent ``scoped_interpreter`` guards is a fatal error. So is
calling `initialize_interpreter` for a second time after the interpreter calling ``initialize_interpreter`` for a second time after the interpreter
has already been initialized. has already been initialized.
Do not use the raw CPython API functions ``Py_Initialize`` and Do not use the raw CPython API functions ``Py_Initialize`` and
@ -241,7 +242,7 @@ global data. All the details can be found in the CPython documentation.
Sub-interpreter support Sub-interpreter support
======================= =======================
Creating multiple copies of `scoped_interpreter` is not possible because it Creating multiple copies of ``scoped_interpreter`` is not possible because it
represents the main Python interpreter. Sub-interpreters are something different represents the main Python interpreter. Sub-interpreters are something different
and they do permit the existence of multiple interpreters. This is an advanced and they do permit the existence of multiple interpreters. This is an advanced
feature of the CPython API and should be handled with care. pybind11 does not feature of the CPython API and should be handled with care. pybind11 does not
@ -257,5 +258,5 @@ We'll just mention a couple of caveats the sub-interpreters support in pybind11:
2. Managing multiple threads, multiple interpreters and the GIL can be 2. Managing multiple threads, multiple interpreters and the GIL can be
challenging and there are several caveats here, even within the pure challenging and there are several caveats here, even within the pure
CPython API (please refer to the Python docs for details). As for CPython API (please refer to the Python docs for details). As for
pybind11, keep in mind that `gil_scoped_release` and `gil_scoped_acquire` pybind11, keep in mind that ``gil_scoped_release`` and ``gil_scoped_acquire``
do not take sub-interpreters into account. do not take sub-interpreters into account.

View File

@ -56,13 +56,15 @@ at its exception handler.
+--------------------------------------+--------------------------------------+ +--------------------------------------+--------------------------------------+
| :class:`pybind11::buffer_error` | ``BufferError`` | | :class:`pybind11::buffer_error` | ``BufferError`` |
+--------------------------------------+--------------------------------------+ +--------------------------------------+--------------------------------------+
| :class:`pybind11::import_error` | ``import_error`` | | :class:`pybind11::import_error` | ``ImportError`` |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::attribute_error` | ``AttributeError`` |
+--------------------------------------+--------------------------------------+ +--------------------------------------+--------------------------------------+
| Any other exception | ``RuntimeError`` | | Any other exception | ``RuntimeError`` |
+--------------------------------------+--------------------------------------+ +--------------------------------------+--------------------------------------+
Exception translation is not bidirectional. That is, *catching* the C++ Exception translation is not bidirectional. That is, *catching* the C++
exceptions defined above above will not trap exceptions that originate from exceptions defined above will not trap exceptions that originate from
Python. For that, catch :class:`pybind11::error_already_set`. See :ref:`below Python. For that, catch :class:`pybind11::error_already_set`. See :ref:`below
<handling_python_exceptions_cpp>` for further details. <handling_python_exceptions_cpp>` for further details.
@ -75,9 +77,10 @@ Registering custom translators
If the default exception conversion policy described above is insufficient, If the default exception conversion policy described above is insufficient,
pybind11 also provides support for registering custom exception translators. pybind11 also provides support for registering custom exception translators.
To register a simple exception conversion that translates a C++ exception into Similar to pybind11 classes, exception translators can be local to the module
a new Python exception using the C++ exception's ``what()`` method, a helper they are defined in or global to the entire python session. To register a simple
function is available: exception conversion that translates a C++ exception into a new Python exception
using the C++ exception's ``what()`` method, a helper function is available:
.. code-block:: cpp .. code-block:: cpp
@ -87,29 +90,39 @@ This call creates a Python exception class with the name ``PyExp`` in the given
module and automatically converts any encountered exceptions of type ``CppExp`` module and automatically converts any encountered exceptions of type ``CppExp``
into Python exceptions of type ``PyExp``. into Python exceptions of type ``PyExp``.
A matching function is available for registering a local exception translator:
.. code-block:: cpp
py::register_local_exception<CppExp>(module, "PyExp");
It is possible to specify base class for the exception using the third It is possible to specify base class for the exception using the third
parameter, a `handle`: parameter, a ``handle``:
.. code-block:: cpp .. code-block:: cpp
py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError); py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
py::register_local_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
Then `PyExp` can be caught both as `PyExp` and `RuntimeError`. Then ``PyExp`` can be caught both as ``PyExp`` and ``RuntimeError``.
The class objects of the built-in Python exceptions are listed in the Python The class objects of the built-in Python exceptions are listed in the Python
documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_. documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_.
The default base class is `PyExc_Exception`. The default base class is ``PyExc_Exception``.
When more advanced exception translation is needed, the function When more advanced exception translation is needed, the functions
``py::register_exception_translator(translator)`` can be used to register ``py::register_exception_translator(translator)`` and
``py::register_local_exception_translator(translator)`` can be used to register
functions that can translate arbitrary exception types (and which may include functions that can translate arbitrary exception types (and which may include
additional logic to do so). The function takes a stateless callable (e.g. a additional logic to do so). The functions takes a stateless callable (e.g. a
function pointer or a lambda function without captured variables) with the call function pointer or a lambda function without captured variables) with the call
signature ``void(std::exception_ptr)``. signature ``void(std::exception_ptr)``.
When a C++ exception is thrown, the registered exception translators are tried When a C++ exception is thrown, the registered exception translators are tried
in reverse order of registration (i.e. the last registered translator gets the in reverse order of registration (i.e. the last registered translator gets the
first shot at handling the exception). first shot at handling the exception). All local translators will be tried
before a global translator is tried.
Inside the translator, ``std::rethrow_exception`` should be used within Inside the translator, ``std::rethrow_exception`` should be used within
a try block to re-throw the exception. One or more catch clauses to catch a try block to re-throw the exception. One or more catch clauses to catch
@ -168,6 +181,53 @@ section.
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``. with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``.
See also: "Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_. See also: "Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
Local vs Global Exception Translators
=====================================
When a global exception translator is registered, it will be applied across all
modules in the reverse order of registration. This can create behavior where the
order of module import influences how exceptions are translated.
If module1 has the following translator:
.. code-block:: cpp
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (const std::invalid_argument &e) {
PyErr_SetString("module1 handled this")
}
}
and module2 has the following similar translator:
.. code-block:: cpp
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (const std::invalid_argument &e) {
PyErr_SetString("module2 handled this")
}
}
then which translator handles the invalid_argument will be determined by the
order that module1 and module2 are imported. Since exception translators are
applied in the reverse order of registration, which ever module was imported
last will "win" and that translator will be applied.
If there are multiple pybind11 modules that share exception types (either
standard built-in or custom) loaded into a single python instance and
consistent error handling behavior is needed, then local translators should be
used.
Changing the previous example to use ``register_local_exception_translator``
would mean that when invalid_argument is thrown in the module2 code, the
module2 translator will always handle it, while in module1, the module1
translator will do the same.
.. _handling_python_exceptions_cpp: .. _handling_python_exceptions_cpp:
Handling exceptions from Python in C++ Handling exceptions from Python in C++
@ -265,6 +325,34 @@ Alternately, to ignore the error, call `PyErr_Clear
Any Python error must be thrown or cleared, or Python/pybind11 will be left in Any Python error must be thrown or cleared, or Python/pybind11 will be left in
an invalid state. an invalid state.
Chaining exceptions ('raise from')
==================================
In Python 3.3 a mechanism for indicating that exceptions were caused by other
exceptions was introduced:
.. code-block:: py
try:
print(1 / 0)
except Exception as exc:
raise RuntimeError("could not divide by zero") from exc
To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It
sets the current python error indicator, so to continue propagating the exception
you should ``throw py::error_already_set()`` (Python 3 only).
.. code-block:: cpp
try {
py::eval("print(1 / 0"));
} catch (py::error_already_set &e) {
py::raise_from(e, PyExc_RuntimeError, "could not divide by zero");
throw py::error_already_set();
}
.. versionadded:: 2.8
.. _unraisable_exceptions: .. _unraisable_exceptions:
Handling unraisable exceptions Handling unraisable exceptions

View File

@ -120,7 +120,7 @@ targeted arguments can be passed through the :class:`cpp_function` constructor:
.. code-block:: cpp .. code-block:: cpp
class_<MyClass>(m, "MyClass") class_<MyClass>(m, "MyClass")
.def_property("data" .def_property("data",
py::cpp_function(&MyClass::getData, py::return_value_policy::copy), py::cpp_function(&MyClass::getData, py::return_value_policy::copy),
py::cpp_function(&MyClass::setData) py::cpp_function(&MyClass::setData)
); );
@ -232,7 +232,7 @@ is equivalent to the following pseudocode:
}); });
The only requirement is that ``T`` is default-constructible, but otherwise any The only requirement is that ``T`` is default-constructible, but otherwise any
scope guard will work. This is very useful in combination with `gil_scoped_release`. scope guard will work. This is very useful in combination with ``gil_scoped_release``.
See :ref:`gil`. See :ref:`gil`.
Multiple guards can also be specified as ``py::call_guard<T1, T2, T3...>``. The Multiple guards can also be specified as ``py::call_guard<T1, T2, T3...>``. The
@ -272,7 +272,7 @@ And used in Python as usual:
.. code-block:: pycon .. code-block:: pycon
>>> print_dict({'foo': 123, 'bar': 'hello'}) >>> print_dict({"foo": 123, "bar": "hello"})
key=foo, value=123 key=foo, value=123
key=bar, value=hello key=bar, value=hello
@ -306,8 +306,9 @@ The class ``py::args`` derives from ``py::tuple`` and ``py::kwargs`` derives
from ``py::dict``. from ``py::dict``.
You may also use just one or the other, and may combine these with other You may also use just one or the other, and may combine these with other
arguments as long as the ``py::args`` and ``py::kwargs`` arguments are the last arguments. Note, however, that ``py::kwargs`` must always be the last argument
arguments accepted by the function. of the function, and ``py::args`` implies that any further arguments are
keyword-only (see :ref:`keyword_only_arguments`).
Please refer to the other examples for details on how to iterate over these, Please refer to the other examples for details on how to iterate over these,
and on how to cast their entries into C++ objects. A demonstration is also and on how to cast their entries into C++ objects. A demonstration is also
@ -366,6 +367,8 @@ like so:
py::class_<MyClass>("MyClass") py::class_<MyClass>("MyClass")
.def("myFunction", py::arg("arg") = static_cast<SomeType *>(nullptr)); .def("myFunction", py::arg("arg") = static_cast<SomeType *>(nullptr));
.. _keyword_only_arguments:
Keyword-only arguments Keyword-only arguments
====================== ======================
@ -377,10 +380,11 @@ argument in a function definition:
def f(a, *, b): # a can be positional or via keyword; b must be via keyword def f(a, *, b): # a can be positional or via keyword; b must be via keyword
pass pass
f(a=1, b=2) # good f(a=1, b=2) # good
f(b=2, a=1) # good f(b=2, a=1) # good
f(1, b=2) # good f(1, b=2) # good
f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given
Pybind11 provides a ``py::kw_only`` object that allows you to implement Pybind11 provides a ``py::kw_only`` object that allows you to implement
the same behaviour by specifying the object between positional and keyword-only the same behaviour by specifying the object between positional and keyword-only
@ -396,6 +400,15 @@ feature does *not* require Python 3 to work.
.. versionadded:: 2.6 .. versionadded:: 2.6
As of pybind11 2.9, a ``py::args`` argument implies that any following arguments
are keyword-only, as if ``py::kw_only()`` had been specified in the same
relative location of the argument list as the ``py::args`` argument. The
``py::kw_only()`` may be included to be explicit about this, but is not
required. (Prior to 2.9 ``py::args`` may only occur at the end of the argument
list, or immediately before a ``py::kwargs`` argument at the end).
.. versionadded:: 2.9
Positional-only arguments Positional-only arguments
========================= =========================
@ -565,3 +578,38 @@ prefers earlier-defined overloads to later-defined ones.
.. versionadded:: 2.6 .. versionadded:: 2.6
The ``py::prepend()`` tag. The ``py::prepend()`` tag.
Binding functions with template parameters
==========================================
You can bind functions that have template parameters. Here's a function:
.. code-block:: cpp
template <typename T>
void set(T t);
C++ templates cannot be instantiated at runtime, so you cannot bind the
non-instantiated function:
.. code-block:: cpp
// BROKEN (this will not compile)
m.def("set", &set);
You must bind each instantiated function template separately. You may bind
each instantiation with the same name, which will be treated the same as
an overloaded function:
.. code-block:: cpp
m.def("set", &set<int>);
m.def("set", &set<std::string>);
Sometimes it's more clear to bind them with separate names, which is also
an option:
.. code-block:: cpp
m.def("setInt", &set<int>);
m.def("setString", &set<std::string>);

View File

@ -84,7 +84,7 @@ could be realized as follows (important changes highlighted):
}); });
} }
The ``call_go`` wrapper can also be simplified using the `call_guard` policy The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy
(see :ref:`call_policies`) which yields the same result: (see :ref:`call_policies`) which yields the same result:
.. code-block:: cpp .. code-block:: cpp

View File

@ -171,6 +171,31 @@ template parameter, and it ensures that non-conforming arguments are converted
into an array satisfying the specified requirements instead of trying the next into an array satisfying the specified requirements instead of trying the next
function overload. function overload.
There are several methods on arrays; the methods listed below under references
work, as well as the following functions based on the NumPy API:
- ``.dtype()`` returns the type of the contained values.
- ``.strides()`` returns a pointer to the strides of the array (optionally pass
an integer axis to get a number).
- ``.flags()`` returns the flag settings. ``.writable()`` and ``.owndata()``
are directly available.
- ``.offset_at()`` returns the offset (optionally pass indices).
- ``.squeeze()`` returns a view with length-1 axes removed.
- ``.view(dtype)`` returns a view of the array with a different dtype.
- ``.reshape({i, j, ...})`` returns a view of the array with a different shape.
``.resize({...})`` is also available.
- ``.index_at(i, j, ...)`` gets the count from the beginning to a given index.
There are also several methods for getting references (described below).
Structured types Structured types
================ ================
@ -233,8 +258,8 @@ by the compiler. The result is returned as a NumPy array of type
.. code-block:: pycon .. code-block:: pycon
>>> x = np.array([[1, 3],[5, 7]]) >>> x = np.array([[1, 3], [5, 7]])
>>> y = np.array([[2, 4],[6, 8]]) >>> y = np.array([[2, 4], [6, 8]])
>>> z = 3 >>> z = 3
>>> result = vectorized_func(x, y, z) >>> result = vectorized_func(x, y, z)
@ -345,21 +370,21 @@ The returned proxy object supports some of the same methods as ``py::array`` so
that it can be used as a drop-in replacement for some existing, index-checked that it can be used as a drop-in replacement for some existing, index-checked
uses of ``py::array``: uses of ``py::array``:
- ``r.ndim()`` returns the number of dimensions - ``.ndim()`` returns the number of dimensions
- ``r.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to - ``.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to
the ``const T`` or ``T`` data, respectively, at the given indices. The the ``const T`` or ``T`` data, respectively, at the given indices. The
latter is only available to proxies obtained via ``a.mutable_unchecked()``. latter is only available to proxies obtained via ``a.mutable_unchecked()``.
- ``itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``. - ``.itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``.
- ``ndim()`` returns the number of dimensions. - ``.ndim()`` returns the number of dimensions.
- ``shape(n)`` returns the size of dimension ``n`` - ``.shape(n)`` returns the size of dimension ``n``
- ``size()`` returns the total number of elements (i.e. the product of the shapes). - ``.size()`` returns the total number of elements (i.e. the product of the shapes).
- ``nbytes()`` returns the number of bytes used by the referenced elements - ``.nbytes()`` returns the number of bytes used by the referenced elements
(i.e. ``itemsize()`` times ``size()``). (i.e. ``itemsize()`` times ``size()``).
.. seealso:: .. seealso::
@ -378,7 +403,7 @@ In Python 2, the syntactic sugar ``...`` is not available, but the singleton
.. code-block:: python .. code-block:: python
a = # a NumPy array a = ... # a NumPy array
b = a[0, ..., 0] b = a[0, ..., 0]
The function ``py::ellipsis()`` function can be used to perform the same The function ``py::ellipsis()`` function can be used to perform the same

View File

@ -20,6 +20,40 @@ Available types include :class:`handle`, :class:`object`, :class:`bool_`,
Be sure to review the :ref:`pytypes_gotchas` before using this heavily in Be sure to review the :ref:`pytypes_gotchas` before using this heavily in
your C++ API. your C++ API.
.. _instantiating_compound_types:
Instantiating compound Python types from C++
============================================
Dictionaries can be initialized in the :class:`dict` constructor:
.. code-block:: cpp
using namespace pybind11::literals; // to bring in the `_a` literal
py::dict d("spam"_a=py::none(), "eggs"_a=42);
A tuple of python objects can be instantiated using :func:`py::make_tuple`:
.. code-block:: cpp
py::tuple tup = py::make_tuple(42, py::none(), "spam");
Each element is converted to a supported Python type.
A `simple namespace`_ can be instantiated using
.. code-block:: cpp
using namespace pybind11::literals; // to bring in the `_a` literal
py::object SimpleNamespace = py::module_::import("types").attr("SimpleNamespace");
py::object ns = SimpleNamespace("spam"_a=py::none(), "eggs"_a=42);
Attributes on a namespace can be modified with the :func:`py::delattr`,
:func:`py::getattr`, and :func:`py::setattr` functions. Simple namespaces can
be useful as lightweight stand-ins for class instances.
.. _simple namespace: https://docs.python.org/3/library/types.html#types.SimpleNamespace
.. _casting_back_and_forth: .. _casting_back_and_forth:
Casting back and forth Casting back and forth
@ -30,7 +64,7 @@ types to Python, which can be done using :func:`py::cast`:
.. code-block:: cpp .. code-block:: cpp
MyClass *cls = ..; MyClass *cls = ...;
py::object obj = py::cast(cls); py::object obj = py::cast(cls);
The reverse direction uses the following syntax: The reverse direction uses the following syntax:
@ -132,6 +166,7 @@ Keyword arguments are also supported. In Python, there is the usual call syntax:
def f(number, say, to): def f(number, say, to):
... # function code ... # function code
f(1234, say="hello", to=some_instance) # keyword call in Python f(1234, say="hello", to=some_instance) # keyword call in Python
In C++, the same call can be made using: In C++, the same call can be made using:

View File

@ -28,7 +28,7 @@ Capturing standard output from ostream
Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print, Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print,
but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr`` but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr``
redirection. Replacing a library's printing with `py::print <print>` may not redirection. Replacing a library's printing with ``py::print <print>`` may not
be feasible. This can be fixed using a guard around the library function that be feasible. This can be fixed using a guard around the library function that
redirects output to the corresponding Python streams: redirects output to the corresponding Python streams:
@ -62,11 +62,11 @@ This method respects flushes on the output streams and will flush if needed
when the scoped guard is destroyed. This allows the output to be redirected in when the scoped guard is destroyed. This allows the output to be redirected in
real time, such as to a Jupyter notebook. The two arguments, the C++ stream and real time, such as to a Jupyter notebook. The two arguments, the C++ stream and
the Python output, are optional, and default to standard output if not given. An the Python output, are optional, and default to standard output if not given. An
extra type, `py::scoped_estream_redirect <scoped_estream_redirect>`, is identical extra type, ``py::scoped_estream_redirect <scoped_estream_redirect>``, is identical
except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with
`py::call_guard`, which allows multiple items, but uses the default constructor: ``py::call_guard``, which allows multiple items, but uses the default constructor:
.. code-block:: py .. code-block:: cpp
// Alternative: Call single function using call guard // Alternative: Call single function using call guard
m.def("noisy_func", &call_noisy_function, m.def("noisy_func", &call_noisy_function,
@ -74,7 +74,7 @@ except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful wi
py::scoped_estream_redirect>()); py::scoped_estream_redirect>());
The redirection can also be done in Python with the addition of a context The redirection can also be done in Python with the addition of a context
manager, using the `py::add_ostream_redirect() <add_ostream_redirect>` function: manager, using the ``py::add_ostream_redirect() <add_ostream_redirect>`` function:
.. code-block:: cpp .. code-block:: cpp
@ -103,7 +103,7 @@ arguments to disable one of the streams if needed.
Evaluating Python expressions from strings and files Evaluating Python expressions from strings and files
==================================================== ====================================================
pybind11 provides the `eval`, `exec` and `eval_file` functions to evaluate pybind11 provides the ``eval``, ``exec`` and ``eval_file`` functions to evaluate
Python expressions and statements. The following example illustrates how they Python expressions and statements. The following example illustrates how they
can be used. can be used.

View File

@ -77,6 +77,7 @@ segmentation fault).
.. code-block:: python .. code-block:: python
from example import Parent from example import Parent
print(Parent().get_child()) print(Parent().get_child())
The problem is that ``Parent::get_child()`` returns a pointer to an instance of The problem is that ``Parent::get_child()`` returns a pointer to an instance of

View File

@ -109,7 +109,7 @@ a file named :file:`example.cpp` with the following contents:
PYBIND11_MODULE(example, m) { PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function which adds two numbers"); m.def("add", &add, "A function that adds two numbers");
} }
.. [#f1] In practice, implementation and binding code will generally be located .. [#f1] In practice, implementation and binding code will generally be located

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import random
import os
import time
import datetime as dt import datetime as dt
import os
import random
nfns = 4 # Functions per class nfns = 4 # Functions per class
nargs = 4 # Arguments per function nargs = 4 # Arguments per function

View File

@ -6,15 +6,460 @@ Changelog
Starting with version 1.8.0, pybind11 releases use a `semantic versioning Starting with version 1.8.0, pybind11 releases use a `semantic versioning
<http://semver.org>`_ policy. <http://semver.org>`_ policy.
v2.8.0 (WIP) Changes will be added here periodically from the "Suggested changelog entry"
------------ block in pull request descriptions.
IN DEVELOPMENT
--------------
Removed support for Python 2.7, Python 3.5, and MSVC 2015. Support for MSVC
2017 is limited due to availability of CI runners; we highly recommend MSVC
2019 or 2022 be used.
New features:
* ``type_caster<std::monostate>`` was added. ``std::monostate`` is a tag type
that allows ``std::variant`` to act as an optional, or allows default
construction of a ``std::variant`` holding a non-default constructible type.
`#3818 <https://github.com/pybind/pybind11/pull/3818>`_
* Support bytearray casting to string.
`#3707 <https://github.com/pybind/pybind11/pull/3707>`_
Changes:
* Python 2 support was removed completely.
`#3688 <https://github.com/pybind/pybind11/pull/3688>`_
* The minimum version for MSVC is now 2017.
`#3722 <https://github.com/pybind/pybind11/pull/3722>`_
* Improve exception handling in python ``str`` bindings.
`#3826 <https://github.com/pybind/pybind11/pull/3826>`_
* The bindings for capsules now have more consistent exception handling.
`#3825 <https://github.com/pybind/pybind11/pull/3825>`_
* Fix exception handling when ``pybind11::weakref()`` fails.
`#3739 <https://github.com/pybind/pybind11/pull/3739>`_
Bug fixes:
* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can be used
to define classes in namespaces other than pybind11.
`#3797 <https://github.com/pybind/pybind11/pull/3797>`_
Build system improvements:
* Add MSVC builds in debug mode to CI.
`#3784 <https://github.com/pybind/pybind11/pull/3784>`_
* MSVC 2022 C++20 coverage was added to GitHub Actions, including Eigen.
`#3732 <https://github.com/pybind/pybind11/pull/3732>`_,
`#3741 <https://github.com/pybind/pybind11/pull/3741>`_
* Avoid ``setup.py <command>`` usage in internal tests.
`#3734 <https://github.com/pybind/pybind11/pull/3734>`_
Backend and tidying up:
* Remove idioms in code comments. Use inclusive language.
`#3809 <https://github.com/pybind/pybind11/pull/3809>`_
Version 2.9.2 (Mar 29, 2022)
----------------------------
Changes:
* Enum now has an ``__index__`` method on Python <3.8 too.
`#3700 <https://github.com/pybind/pybind11/pull/3700>`_
* Local internals are now cleared after finalizing the interpreter.
`#3744 <https://github.com/pybind/pybind11/pull/3744>`_
Bug fixes:
* Better support for Python 3.11 alphas.
`#3694 <https://github.com/pybind/pybind11/pull/3694>`_
* ``PYBIND11_TYPE_CASTER`` now uses fully qualified symbols, so it can be used
outside of ``pybind11::detail``.
`#3758 <https://github.com/pybind/pybind11/pull/3758>`_
* Some fixes for PyPy 3.9.
`#3768 <https://github.com/pybind/pybind11/pull/3768>`_
* Fixed a potential memleak in PyPy in ``get_type_override``.
`#3774 <https://github.com/pybind/pybind11/pull/3774>`_
* Fix usage of ``VISIBILITY_INLINES_HIDDEN``.
`#3721 <https://github.com/pybind/pybind11/pull/3721>`_
Build system improvements:
* Uses ``sysconfig`` module to determine installation locations on Python >=
3.10, instead of ``distutils`` which has been deprecated.
`#3764 <https://github.com/pybind/pybind11/pull/3764>`_
* Support Catch 2.13.5+ (supporting GLIBC 2.34+).
`#3679 <https://github.com/pybind/pybind11/pull/3679>`_
* Fix test failures with numpy 1.22 by ignoring whitespace when comparing
``str()`` of dtypes.
`#3682 <https://github.com/pybind/pybind11/pull/3682>`_
Backend and tidying up:
* clang-tidy: added ``readability-qualified-auto``,
``readability-braces-around-statements``,
``cppcoreguidelines-prefer-member-initializer``,
``clang-analyzer-optin.performance.Padding``,
``cppcoreguidelines-pro-type-static-cast-downcast``, and
``readability-inconsistent-declaration-parameter-name``.
`#3702 <https://github.com/pybind/pybind11/pull/3702>`_,
`#3699 <https://github.com/pybind/pybind11/pull/3699>`_,
`#3716 <https://github.com/pybind/pybind11/pull/3716>`_,
`#3709 <https://github.com/pybind/pybind11/pull/3709>`_
* clang-format was added to the pre-commit actions, and the entire code base
automatically reformatted (after several iterations preparing for this leap).
`#3713 <https://github.com/pybind/pybind11/pull/3713>`_
Version 2.9.1 (Feb 2, 2022)
---------------------------
Changes:
* If possible, attach Python exception with ``py::raise_from`` to ``TypeError``
when casting from C++ to Python. This will give additional info if Python
exceptions occur in the caster. Adds a test case of trying to convert a set
from C++ to Python when the hash function is not defined in Python.
`#3605 <https://github.com/pybind/pybind11/pull/3605>`_
* Add a mapping of C++11 nested exceptions to their Python exception
equivalent using ``py::raise_from``. This attaches the nested exceptions in
Python using the ``__cause__`` field.
`#3608 <https://github.com/pybind/pybind11/pull/3608>`_
* Propagate Python exception traceback using ``raise_from`` if a pybind11
function runs out of overloads.
`#3671 <https://github.com/pybind/pybind11/pull/3671>`_
* ``py::multiple_inheritance`` is now only needed when C++ bases are hidden
from pybind11.
`#3650 <https://github.com/pybind/pybind11/pull/3650>`_ and
`#3659 <https://github.com/pybind/pybind11/pull/3659>`_
Bug fixes:
* Remove a boolean cast in ``numpy.h`` that causes MSVC C4800 warnings when
compiling against Python 3.10 or newer.
`#3669 <https://github.com/pybind/pybind11/pull/3669>`_
* Render ``py::bool_`` and ``py::float_`` as ``bool`` and ``float``
respectively.
`#3622 <https://github.com/pybind/pybind11/pull/3622>`_
Build system improvements:
* Fix CMake extension suffix computation on Python 3.10+.
`#3663 <https://github.com/pybind/pybind11/pull/3663>`_
* Allow ``CMAKE_ARGS`` to override CMake args in pybind11's own ``setup.py``.
`#3577 <https://github.com/pybind/pybind11/pull/3577>`_
* Remove a few deprecated c-headers.
`#3610 <https://github.com/pybind/pybind11/pull/3610>`_
* More uniform handling of test targets.
`#3590 <https://github.com/pybind/pybind11/pull/3590>`_
* Add clang-tidy readability check to catch potentially swapped function args.
`#3611 <https://github.com/pybind/pybind11/pull/3611>`_
Version 2.9.0 (Dec 28, 2021)
----------------------------
This is the last version to support Python 2.7 and 3.5.
New Features:
* Allow ``py::args`` to be followed by other arguments; the remaining arguments
are implicitly keyword-only, as if a ``py::kw_only{}`` annotation had been
used.
`#3402 <https://github.com/pybind/pybind11/pull/3402>`_
Changes:
* Make str/bytes/memoryview more interoperable with ``std::string_view``.
`#3521 <https://github.com/pybind/pybind11/pull/3521>`_
* Replace ``_`` with ``const_name`` in internals, avoid defining ``pybind::_``
if ``_`` defined as macro (common gettext usage)
`#3423 <https://github.com/pybind/pybind11/pull/3423>`_
Bug fixes:
* Fix a rare warning about extra copy in an Eigen constructor.
`#3486 <https://github.com/pybind/pybind11/pull/3486>`_
* Fix caching of the C++ overrides.
`#3465 <https://github.com/pybind/pybind11/pull/3465>`_
* Add missing ``std::forward`` calls to some ``cpp_function`` overloads.
`#3443 <https://github.com/pybind/pybind11/pull/3443>`_
* Support PyPy 7.3.7 and the PyPy3.8 beta. Test python-3.11 on PRs with the
``python dev`` label.
`#3419 <https://github.com/pybind/pybind11/pull/3419>`_
* Replace usage of deprecated ``Eigen::MappedSparseMatrix`` with
``Eigen::Map<Eigen::SparseMatrix<...>>`` for Eigen 3.3+.
`#3499 <https://github.com/pybind/pybind11/pull/3499>`_
* Tweaks to support Microsoft Visual Studio 2022.
`#3497 <https://github.com/pybind/pybind11/pull/3497>`_
Build system improvements:
* Nicer CMake printout and IDE organisation for pybind11's own tests.
`#3479 <https://github.com/pybind/pybind11/pull/3479>`_
* CMake: report version type as part of the version string to avoid a spurious
space in the package status message.
`#3472 <https://github.com/pybind/pybind11/pull/3472>`_
* Flags starting with ``-g`` in ``$CFLAGS`` and ``$CPPFLAGS`` are no longer
overridden by ``.Pybind11Extension``.
`#3436 <https://github.com/pybind/pybind11/pull/3436>`_
* Ensure ThreadPool is closed in ``setup_helpers``.
`#3548 <https://github.com/pybind/pybind11/pull/3548>`_
* Avoid LTS on ``mips64`` and ``ppc64le`` (reported broken).
`#3557 <https://github.com/pybind/pybind11/pull/3557>`_
v2.8.1 (Oct 27, 2021)
---------------------
Changes and additions:
* The simple namespace creation shortcut added in 2.8.0 was deprecated due to
usage of CPython internal API, and will be removed soon. Use
``py::module_::import("types").attr("SimpleNamespace")``.
`#3374 <https://github.com/pybinyyd/pybind11/pull/3374>`_
* Add C++ Exception type to throw and catch ``AttributeError``. Useful for
defining custom ``__setattr__`` and ``__getattr__`` methods.
`#3387 <https://github.com/pybind/pybind11/pull/3387>`_
Fixes:
* Fixed the potential for dangling references when using properties with
``std::optional`` types.
`#3376 <https://github.com/pybind/pybind11/pull/3376>`_
* Modernize usage of ``PyCodeObject`` on Python 3.9+ (moving toward support for
Python 3.11a1)
`#3368 <https://github.com/pybind/pybind11/pull/3368>`_
* A long-standing bug in ``eigen.h`` was fixed (originally PR #3343). The bug
was unmasked by newly added ``static_assert``'s in the Eigen 3.4.0 release.
`#3352 <https://github.com/pybind/pybind11/pull/3352>`_
* Support multiple raw inclusion of CMake helper files (Conan.io does this for
multi-config generators).
`#3420 <https://github.com/pybind/pybind11/pull/3420>`_
* Fix harmless warning on upcoming CMake 3.22.
`#3368 <https://github.com/pybind/pybind11/pull/3368>`_
* Fix 2.8.0 regression with MSVC 2017 + C++17 mode + Python 3.
`#3407 <https://github.com/pybind/pybind11/pull/3407>`_
* Fix 2.8.0 regression that caused undefined behavior (typically
segfaults) in ``make_key_iterator``/``make_value_iterator`` if dereferencing
the iterator returned a temporary value instead of a reference.
`#3348 <https://github.com/pybind/pybind11/pull/3348>`_
v2.8.0 (Oct 4, 2021)
--------------------
New features:
* Added ``py::raise_from`` to enable chaining exceptions.
`#3215 <https://github.com/pybind/pybind11/pull/3215>`_
* Allow exception translators to be optionally registered local to a module * Allow exception translators to be optionally registered local to a module
instead of applying globally across all pybind11 modules. Use instead of applying globally across all pybind11 modules. Use
``register_local_exception_translator(ExceptionTranslator&& translator)`` ``register_local_exception_translator(ExceptionTranslator&& translator)``
instead of ``register_exception_translator(ExceptionTranslator&& instead of ``register_exception_translator(ExceptionTranslator&&
translator)`` to keep your exception remapping code local to the module. translator)`` to keep your exception remapping code local to the module.
`#2650 <https://github.com/pybind/pybind11/pull/2650>`_ `#2650 <https://github.com/pybinyyd/pybind11/pull/2650>`_
* Add ``make_simple_namespace`` function for instantiating Python
``SimpleNamespace`` objects. **Deprecated in 2.8.1.**
`#2840 <https://github.com/pybind/pybind11/pull/2840>`_
* ``pybind11::scoped_interpreter`` and ``initialize_interpreter`` have new
arguments to allow ``sys.argv`` initialization.
`#2341 <https://github.com/pybind/pybind11/pull/2341>`_
* Allow Python builtins to be used as callbacks in CPython.
`#1413 <https://github.com/pybind/pybind11/pull/1413>`_
* Added ``view`` to view arrays with a different datatype.
`#987 <https://github.com/pybind/pybind11/pull/987>`_
* Implemented ``reshape`` on arrays.
`#984 <https://github.com/pybind/pybind11/pull/984>`_
* Enable defining custom ``__new__`` methods on classes by fixing bug
preventing overriding methods if they have non-pybind11 siblings.
`#3265 <https://github.com/pybind/pybind11/pull/3265>`_
* Add ``make_value_iterator()``, and fix ``make_key_iterator()`` to return
references instead of copies.
`#3293 <https://github.com/pybind/pybind11/pull/3293>`_
* Improve the classes generated by ``bind_map``: `#3310 <https://github.com/pybind/pybind11/pull/3310>`_
* Change ``.items`` from an iterator to a dictionary view.
* Add ``.keys`` and ``.values`` (both dictionary views).
* Allow ``__contains__`` to take any object.
* ``pybind11::custom_type_setup`` was added, for customizing the
``PyHeapTypeObject`` corresponding to a class, which may be useful for
enabling garbage collection support, among other things.
`#3287 <https://github.com/pybind/pybind11/pull/3287>`_
Changes:
* Set ``__file__`` constant when running ``eval_file`` in an embedded interpreter.
`#3233 <https://github.com/pybind/pybind11/pull/3233>`_
* Python objects and (C++17) ``std::optional`` now accepted in ``py::slice``
constructor.
`#1101 <https://github.com/pybind/pybind11/pull/1101>`_
* The pybind11 proxy types ``str``, ``bytes``, ``bytearray``, ``tuple``,
``list`` now consistently support passing ``ssize_t`` values for sizes and
indexes. Previously, only ``size_t`` was accepted in several interfaces.
`#3219 <https://github.com/pybind/pybind11/pull/3219>`_
* Avoid evaluating ``PYBIND11_TLS_REPLACE_VALUE`` arguments more than once.
`#3290 <https://github.com/pybind/pybind11/pull/3290>`_
Fixes:
* Bug fix: enum value's ``__int__`` returning non-int when underlying type is
bool or of char type.
`#1334 <https://github.com/pybind/pybind11/pull/1334>`_
* Fixes bug in setting error state in Capsule's pointer methods.
`#3261 <https://github.com/pybind/pybind11/pull/3261>`_
* A long-standing memory leak in ``py::cpp_function::initialize`` was fixed.
`#3229 <https://github.com/pybind/pybind11/pull/3229>`_
* Fixes thread safety for some ``pybind11::type_caster`` which require lifetime
extension, such as for ``std::string_view``.
`#3237 <https://github.com/pybind/pybind11/pull/3237>`_
* Restore compatibility with gcc 4.8.4 as distributed by ubuntu-trusty, linuxmint-17.
`#3270 <https://github.com/pybind/pybind11/pull/3270>`_
Build system improvements:
* Fix regression in CMake Python package config: improper use of absolute path.
`#3144 <https://github.com/pybind/pybind11/pull/3144>`_
* Cached Python version information could become stale when CMake was re-run
with a different Python version. The build system now detects this and
updates this information.
`#3299 <https://github.com/pybind/pybind11/pull/3299>`_
* Specified UTF8-encoding in setup.py calls of open().
`#3137 <https://github.com/pybind/pybind11/pull/3137>`_
* Fix a harmless warning from CMake 3.21 with the classic Python discovery.
`#3220 <https://github.com/pybind/pybind11/pull/3220>`_
* Eigen repo and version can now be specified as cmake options.
`#3324 <https://github.com/pybind/pybind11/pull/3324>`_
Backend and tidying up:
* Reduced thread-local storage required for keeping alive temporary data for
type conversion to one key per ABI version, rather than one key per extension
module. This makes the total thread-local storage required by pybind11 2
keys per ABI version.
`#3275 <https://github.com/pybind/pybind11/pull/3275>`_
* Optimize NumPy array construction with additional moves.
`#3183 <https://github.com/pybind/pybind11/pull/3183>`_
* Conversion to ``std::string`` and ``std::string_view`` now avoids making an
extra copy of the data on Python >= 3.3.
`#3257 <https://github.com/pybind/pybind11/pull/3257>`_
* Remove const modifier from certain C++ methods on Python collections
(``list``, ``set``, ``dict``) such as (``clear()``, ``append()``,
``insert()``, etc...) and annotated them with ``py-non-const``.
* Enable readability ``clang-tidy-const-return`` and remove useless consts.
`#3254 <https://github.com/pybind/pybind11/pull/3254>`_
`#3194 <https://github.com/pybind/pybind11/pull/3194>`_
* The clang-tidy ``google-explicit-constructor`` option was enabled.
`#3250 <https://github.com/pybind/pybind11/pull/3250>`_
* Mark a pytype move constructor as noexcept (perf).
`#3236 <https://github.com/pybind/pybind11/pull/3236>`_
* Enable clang-tidy check to guard against inheritance slicing.
`#3210 <https://github.com/pybind/pybind11/pull/3210>`_
* Legacy warning suppression pragma were removed from eigen.h. On Unix
platforms, please use -isystem for Eigen include directories, to suppress
compiler warnings originating from Eigen headers. Note that CMake does this
by default. No adjustments are needed for Windows.
`#3198 <https://github.com/pybind/pybind11/pull/3198>`_
* Format pybind11 with isort consistent ordering of imports
`#3195 <https://github.com/pybind/pybind11/pull/3195>`_
* The warnings-suppression "pragma clamp" at the top/bottom of pybind11 was
removed, clearing the path to refactoring and IWYU cleanup.
`#3186 <https://github.com/pybind/pybind11/pull/3186>`_
* Enable most bugprone checks in clang-tidy and fix the found potential bugs
and poor coding styles.
`#3166 <https://github.com/pybind/pybind11/pull/3166>`_
* Add ``clang-tidy-readability`` rules to make boolean casts explicit improving
code readability. Also enabled other misc and readability clang-tidy checks.
`#3148 <https://github.com/pybind/pybind11/pull/3148>`_
* Move object in ``.pop()`` for list.
`#3116 <https://github.com/pybind/pybind11/pull/3116>`_
v2.7.1 (Aug 3, 2021) v2.7.1 (Aug 3, 2021)
--------------------- ---------------------
@ -87,8 +532,8 @@ New features:
Changes: Changes:
* ``py::str`` changed to exclusively hold `PyUnicodeObject`. Previously * ``py::str`` changed to exclusively hold ``PyUnicodeObject``. Previously
``py::str`` could also hold `bytes`, which is probably surprising, was ``py::str`` could also hold ``bytes``, which is probably surprising, was
never documented, and can mask bugs (e.g. accidental use of ``py::str`` never documented, and can mask bugs (e.g. accidental use of ``py::str``
instead of ``py::bytes``). instead of ``py::bytes``).
`#2409 <https://github.com/pybind/pybind11/pull/2409>`_ `#2409 <https://github.com/pybind/pybind11/pull/2409>`_
@ -1028,6 +1473,7 @@ v2.2.0 (August 31, 2017)
from cpp_module import CppBase1, CppBase2 from cpp_module import CppBase1, CppBase2
class PyDerived(CppBase1, CppBase2): class PyDerived(CppBase1, CppBase2):
def __init__(self): def __init__(self):
CppBase1.__init__(self) # C++ bases must be initialized explicitly CppBase1.__init__(self) # C++ bases must be initialized explicitly
@ -1240,7 +1686,7 @@ v2.2.0 (August 31, 2017)
* Intel C++ compiler compatibility fixes. * Intel C++ compiler compatibility fixes.
`#937 <https://github.com/pybind/pybind11/pull/937>`_. `#937 <https://github.com/pybind/pybind11/pull/937>`_.
* Fixed implicit conversion of `py::enum_` to integer types on Python 2.7. * Fixed implicit conversion of ``py::enum_`` to integer types on Python 2.7.
`#821 <https://github.com/pybind/pybind11/pull/821>`_. `#821 <https://github.com/pybind/pybind11/pull/821>`_.
* Added ``py::hash`` to fetch the hash value of Python objects, and * Added ``py::hash`` to fetch the hash value of Python objects, and

View File

@ -44,12 +44,12 @@ interactive Python session demonstrating this example is shown below:
% python % python
>>> import example >>> import example
>>> p = example.Pet('Molly') >>> p = example.Pet("Molly")
>>> print(p) >>> print(p)
<example.Pet object at 0x10cd98060> <example.Pet object at 0x10cd98060>
>>> p.getName() >>> p.getName()
u'Molly' u'Molly'
>>> p.setName('Charly') >>> p.setName("Charly")
>>> p.getName() >>> p.getName()
u'Charly' u'Charly'
@ -122,10 +122,10 @@ This makes it possible to write
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Pet('Molly') >>> p = example.Pet("Molly")
>>> p.name >>> p.name
u'Molly' u'Molly'
>>> p.name = 'Charly' >>> p.name = "Charly"
>>> p.name >>> p.name
u'Charly' u'Charly'
@ -174,10 +174,10 @@ Native Python classes can pick up new attributes dynamically:
.. code-block:: pycon .. code-block:: pycon
>>> class Pet: >>> class Pet:
... name = 'Molly' ... name = "Molly"
... ...
>>> p = Pet() >>> p = Pet()
>>> p.name = 'Charly' # overwrite existing >>> p.name = "Charly" # overwrite existing
>>> p.age = 2 # dynamically add a new attribute >>> p.age = 2 # dynamically add a new attribute
By default, classes exported from C++ do not support this and the only writable By default, classes exported from C++ do not support this and the only writable
@ -195,7 +195,7 @@ Trying to set any other attribute results in an error:
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Pet() >>> p = example.Pet()
>>> p.name = 'Charly' # OK, attribute defined in C++ >>> p.name = "Charly" # OK, attribute defined in C++
>>> p.age = 2 # fail >>> p.age = 2 # fail
AttributeError: 'Pet' object has no attribute 'age' AttributeError: 'Pet' object has no attribute 'age'
@ -213,7 +213,7 @@ Now everything works as expected:
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Pet() >>> p = example.Pet()
>>> p.name = 'Charly' # OK, overwrite value in C++ >>> p.name = "Charly" # OK, overwrite value in C++
>>> p.age = 2 # OK, dynamically add a new attribute >>> p.age = 2 # OK, dynamically add a new attribute
>>> p.__dict__ # just like a native Python class >>> p.__dict__ # just like a native Python class
{'age': 2} {'age': 2}
@ -280,7 +280,7 @@ expose fields and methods of both types:
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Dog('Molly') >>> p = example.Dog("Molly")
>>> p.name >>> p.name
u'Molly' u'Molly'
>>> p.bark() >>> p.bark()
@ -446,8 +446,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth
Enumerations and internal types Enumerations and internal types
=============================== ===============================
Let's now suppose that the example class contains an internal enumeration type, Let's now suppose that the example class contains internal types like enumerations, e.g.:
e.g.:
.. code-block:: cpp .. code-block:: cpp
@ -457,10 +456,15 @@ e.g.:
Cat Cat
}; };
struct Attributes {
float age = 0;
};
Pet(const std::string &name, Kind type) : name(name), type(type) { } Pet(const std::string &name, Kind type) : name(name), type(type) { }
std::string name; std::string name;
Kind type; Kind type;
Attributes attr;
}; };
The binding code for this example looks as follows: The binding code for this example looks as follows:
@ -471,22 +475,28 @@ The binding code for this example looks as follows:
pet.def(py::init<const std::string &, Pet::Kind>()) pet.def(py::init<const std::string &, Pet::Kind>())
.def_readwrite("name", &Pet::name) .def_readwrite("name", &Pet::name)
.def_readwrite("type", &Pet::type); .def_readwrite("type", &Pet::type)
.def_readwrite("attr", &Pet::attr);
py::enum_<Pet::Kind>(pet, "Kind") py::enum_<Pet::Kind>(pet, "Kind")
.value("Dog", Pet::Kind::Dog) .value("Dog", Pet::Kind::Dog)
.value("Cat", Pet::Kind::Cat) .value("Cat", Pet::Kind::Cat)
.export_values(); .export_values();
To ensure that the ``Kind`` type is created within the scope of ``Pet``, the py::class_<Pet::Attributes> attributes(pet, "Attributes")
``pet`` :class:`class_` instance must be supplied to the :class:`enum_`. .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 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 into the parent scope, which should be skipped for newer C++11-style strongly
typed enums. typed enums.
.. code-block:: pycon .. code-block:: pycon
>>> p = Pet('Lucy', Pet.Cat) >>> p = Pet("Lucy", Pet.Cat)
>>> p.type >>> p.type
Kind.Cat Kind.Cat
>>> int(p.type) >>> int(p.type)
@ -508,7 +518,7 @@ The ``name`` property returns the name of the enum value as a unicode string.
.. code-block:: pycon .. code-block:: pycon
>>> p = Pet( "Lucy", Pet.Cat ) >>> p = Pet("Lucy", Pet.Cat)
>>> pet_type = p.type >>> pet_type = p.type
>>> pet_type >>> pet_type
Pet.Cat Pet.Cat

View File

@ -42,10 +42,7 @@ An example of a ``setup.py`` using pybind11's helpers:
), ),
] ]
setup( setup(..., ext_modules=ext_modules)
...,
ext_modules=ext_modules
)
If you want to do an automatic search for the highest supported C++ standard, If you want to do an automatic search for the highest supported C++ standard,
that is supported via a ``build_ext`` command override; it will only affect that is supported via a ``build_ext`` command override; it will only affect
@ -64,11 +61,7 @@ that is supported via a ``build_ext`` command override; it will only affect
), ),
] ]
setup( setup(..., cmdclass={"build_ext": build_ext}, ext_modules=ext_modules)
...,
cmdclass={"build_ext": build_ext},
ext_modules=ext_modules
)
If you have single-file extension modules that are directly stored in the If you have single-file extension modules that are directly stored in the
Python source tree (``foo.cpp`` in the same directory as where a ``foo.py`` Python source tree (``foo.cpp`` in the same directory as where a ``foo.py``
@ -113,7 +106,7 @@ with the following:
from pybind11.setup_helpers import ParallelCompile, naive_recompile from pybind11.setup_helpers import ParallelCompile, naive_recompile
SmartCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install()
If you have a more complex build, you can implement a smarter function and pass If you have a more complex build, you can implement a smarter function and pass
@ -347,7 +340,7 @@ standard explicitly with
set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ version selection") # or 11, 14, 17, 20 set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ version selection") # or 11, 14, 17, 20
set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported
set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensionsn off set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensions off
The variables can also be set when calling CMake from the command line using The variables can also be set when calling CMake from the command line using
the ``-D<variable>=<value>`` flag. You can also manually set ``CXX_STANDARD`` the ``-D<variable>=<value>`` flag. You can also manually set ``CXX_STANDARD``
@ -482,7 +475,7 @@ available in all modes. The targets provided are:
Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper`` Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper``
``pybind11::embed`` ``pybind11::embed``
Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Embed`` (FindPython) or Python libs Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs
``pybind11::lto`` / ``pybind11::thin_lto`` ``pybind11::lto`` / ``pybind11::thin_lto``
An alternative to `INTERPROCEDURAL_OPTIMIZATION` for adding link-time optimization. An alternative to `INTERPROCEDURAL_OPTIMIZATION` for adding link-time optimization.

View File

@ -13,12 +13,11 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
import sys
import os import os
import shlex
import subprocess
from pathlib import Path
import re import re
import subprocess
import sys
from pathlib import Path
DIR = Path(__file__).parent.resolve() DIR = Path(__file__).parent.resolve()

View File

@ -54,7 +54,7 @@ provided by the caller -- in fact, it does nothing at all.
.. code-block:: python .. code-block:: python
def increment(i): def increment(i):
i += 1 # nope.. i += 1 # nope..
pybind11 is also affected by such language-level conventions, which means that pybind11 is also affected by such language-level conventions, which means that
binding ``increment`` or ``increment_ptr`` will also create Python functions binding ``increment`` or ``increment_ptr`` will also create Python functions

View File

@ -63,6 +63,9 @@ Convenience functions converting to Python types
.. doxygenfunction:: make_key_iterator(Iterator, Sentinel, Extra &&...) .. doxygenfunction:: make_key_iterator(Iterator, Sentinel, Extra &&...)
.. doxygenfunction:: make_key_iterator(Type &, Extra&&...) .. doxygenfunction:: make_key_iterator(Type &, Extra&&...)
.. doxygenfunction:: make_value_iterator(Iterator, Sentinel, Extra &&...)
.. doxygenfunction:: make_value_iterator(Type &, Extra&&...)
.. _extras: .. _extras:
Passing extra arguments to ``def`` or ``class_`` Passing extra arguments to ``def`` or ``class_``

View File

@ -22,11 +22,15 @@ the version just below.
To release a new version of pybind11: To release a new version of pybind11:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you don't have nox, you should either use ``pipx run nox`` instead, or use
``pipx install nox`` or ``brew install nox`` (Unix).
- Update the version number - Update the version number
- Update ``PYBIND11_VERSION_MAJOR`` etc. in - Update ``PYBIND11_VERSION_MAJOR`` etc. in
``include/pybind11/detail/common.h``. PATCH should be a simple integer. ``include/pybind11/detail/common.h``. PATCH should be a simple integer.
- Update the version HEX just below, as well. - Update the version HEX just below, as well.
- Update ``pybind11/_version.py`` (match above) - Update ``pybind11/_version.py`` (match above)
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
- Ensure that all the information in ``setup.cfg`` is up-to-date, like - Ensure that all the information in ``setup.cfg`` is up-to-date, like
supported Python versions. supported Python versions.
- Add release date in ``docs/changelog.rst``. - Add release date in ``docs/changelog.rst``.
@ -49,13 +53,13 @@ To release a new version of pybind11:
- Make a GitHub release (this shows up in the UI, sends new release - Make a GitHub release (this shows up in the UI, sends new release
notifications to users watching releases, and also uploads PyPI packages). notifications to users watching releases, and also uploads PyPI packages).
(Note: if you do not use an existing tag, this creates a new lightweight tag (Note: if you do not use an existing tag, this creates a new lightweight tag
for you, so you could skip the above step). for you, so you could skip the above step.)
- GUI method: click "Create a new release" on the far right, fill in the tag - GUI method: Under `releases <https://github.com/pybind/pybind11/releases>`_
name (if you didn't tag above, it will be made here), fill in a release click "Draft a new release" on the far right, fill in the tag name
name like "Version X.Y.Z", and optionally copy-and-paste the changelog into (if you didn't tag above, it will be made here), fill in a release name
the description (processed as markdown by Pandoc). Check "pre-release" if like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog
this is a beta/RC. You can get partway there with into the description (usually ``cat docs/changelog.rst | pandoc -f rst -t gfm``).
``cat docs/changelog.rst | pandoc -f rst -t gfm``. Check "pre-release" if this is a beta/RC.
- CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"`` - CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"``
If this is a pre-release, add ``-p``. If this is a pre-release, add ``-p``.
@ -64,6 +68,7 @@ To release a new version of pybind11:
- Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to - Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to
``0.dev1`` and increment MINOR). ``0.dev1`` and increment MINOR).
- Update ``_version.py`` to match - Update ``_version.py`` to match
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
- Add a spot for in-development updates in ``docs/changelog.rst``. - Add a spot for in-development updates in ``docs/changelog.rst``.
- ``git add``, ``git commit``, ``git push`` - ``git add``, ``git commit``, ``git push``
@ -71,7 +76,7 @@ If a version branch is updated, remember to set PATCH to ``1.dev1``.
If you'd like to bump homebrew, run: If you'd like to bump homebrew, run:
.. code-block:: .. code-block:: console
brew bump-formula-pr --url https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz brew bump-formula-pr --url https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz
@ -86,9 +91,7 @@ If you need to manually upload releases, you can download the releases from the
.. code-block:: bash .. code-block:: bash
python3 -m pip install build nox -s build
python3 -m build
PYBIND11_SDIST_GLOBAL=1 python3 -m build
twine upload dist/* twine upload dist/*
This makes SDists and wheels, and the final line uploads them. This makes SDists and wheels, and the final line uploads them.

View File

@ -1,8 +1,5 @@
breathe==4.26.1 breathe==4.31.0
# docutils 0.17 breaks HTML tags & RTD theme sphinx==3.5.4
# https://github.com/sphinx-doc/sphinx/issues/9001 sphinx_rtd_theme==1.0.0
docutils==0.16 sphinxcontrib-moderncmakedomain==3.19
sphinx==3.3.1 sphinxcontrib-svg2pdfconverter==1.1.1
sphinx_rtd_theme==0.5.0
sphinxcontrib-moderncmakedomain==3.17
sphinxcontrib-svg2pdfconverter==1.1.0

View File

@ -8,7 +8,21 @@ to a new version. But it goes into more detail. This includes things like
deprecated APIs and their replacements, build system changes, general code deprecated APIs and their replacements, build system changes, general code
modernization and other useful information. modernization and other useful information.
.. _upgrade-guide-2.6: .. _upgrade-guide-2.9:
v2.9
====
* Any usage of the recently added ``py::make_simple_namespace`` should be
converted to using ``py::module_::import("types").attr("SimpleNamespace")``
instead.
* The use of ``_`` in custom type casters can now be replaced with the more
readable ``const_name`` instead. The old ``_`` shortcut has been retained
unless it is being used as a macro (like for gettext).
.. _upgrade-guide-2.7:
v2.7 v2.7
==== ====
@ -34,6 +48,7 @@ to be common:
careful review and custom fixes. careful review and custom fixes.
.. _upgrade-guide-2.6:
v2.6 v2.6
==== ====

View File

@ -12,50 +12,70 @@
#include "cast.h" #include "cast.h"
#include <functional>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
/// \addtogroup annotations /// \addtogroup annotations
/// @{ /// @{
/// Annotation for methods /// Annotation for methods
struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; struct is_method {
handle class_;
explicit is_method(const handle &c) : class_(c) {}
};
/// Annotation for operators /// Annotation for operators
struct is_operator { }; struct is_operator {};
/// Annotation for classes that cannot be subclassed /// Annotation for classes that cannot be subclassed
struct is_final { }; struct is_final {};
/// Annotation for parent scope /// Annotation for parent scope
struct scope { handle value; scope(const handle &s) : value(s) { } }; struct scope {
handle value;
explicit scope(const handle &s) : value(s) {}
};
/// Annotation for documentation /// Annotation for documentation
struct doc { const char *value; doc(const char *value) : value(value) { } }; struct doc {
const char *value;
explicit doc(const char *value) : value(value) {}
};
/// Annotation for function names /// Annotation for function names
struct name { const char *value; name(const char *value) : value(value) { } }; struct name {
const char *value;
explicit name(const char *value) : value(value) {}
};
/// Annotation indicating that a function is an overload associated with a given "sibling" /// Annotation indicating that a function is an overload associated with a given "sibling"
struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; struct sibling {
handle value;
explicit sibling(const handle &value) : value(value.ptr()) {}
};
/// Annotation indicating that a class derives from another given type /// Annotation indicating that a class derives from another given type
template <typename T> struct base { template <typename T>
struct base {
PYBIND11_DEPRECATED("base<T>() was deprecated in favor of specifying 'T' as a template argument to class_") PYBIND11_DEPRECATED(
base() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute "base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
base() {} // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
}; };
/// Keep patient alive while nurse lives /// Keep patient alive while nurse lives
template <size_t Nurse, size_t Patient> struct keep_alive { }; template <size_t Nurse, size_t Patient>
struct keep_alive {};
/// Annotation indicating that a class is involved in a multiple inheritance relationship /// Annotation indicating that a class is involved in a multiple inheritance relationship
struct multiple_inheritance { }; struct multiple_inheritance {};
/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class /// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class
struct dynamic_attr { }; struct dynamic_attr {};
/// Annotation which enables the buffer protocol for a type /// Annotation which enables the buffer protocol for a type
struct buffer_protocol { }; struct buffer_protocol {};
/// Annotation which requests that a special metaclass is created for a type /// Annotation which requests that a special metaclass is created for a type
struct metaclass { struct metaclass {
@ -66,17 +86,37 @@ struct metaclass {
metaclass() {} metaclass() {}
/// Override pybind11's default metaclass /// Override pybind11's default metaclass
explicit metaclass(handle value) : value(value) { } explicit metaclass(handle value) : value(value) {}
};
/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that
/// may be used to customize the Python type.
///
/// The callback is invoked immediately before `PyType_Ready`.
///
/// Note: This is an advanced interface, and uses of it may require changes to
/// work with later versions of pybind11. You may wish to consult the
/// implementation of `make_new_python_type` in `detail/classes.h` to understand
/// the context in which the callback will be run.
struct custom_type_setup {
using callback = std::function<void(PyHeapTypeObject *heap_type)>;
explicit custom_type_setup(callback value) : value(std::move(value)) {}
callback value;
}; };
/// Annotation that marks a class as local to the module: /// Annotation that marks a class as local to the module:
struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; struct module_local {
const bool value;
constexpr explicit module_local(bool v = true) : value(v) {}
};
/// Annotation to mark enums as an arithmetic type /// Annotation to mark enums as an arithmetic type
struct arithmetic { }; struct arithmetic {};
/// Mark a function for addition at the beginning of the existing overload chain instead of the end /// Mark a function for addition at the beginning of the existing overload chain instead of the end
struct prepend { }; struct prepend {};
/** \rst /** \rst
A call policy which places one or more guard variables (``Ts...``) around the function call. A call policy which places one or more guard variables (``Ts...``) around the function call.
@ -96,9 +136,13 @@ struct prepend { };
return foo(args...); // forwarded arguments return foo(args...); // forwarded arguments
}); });
\endrst */ \endrst */
template <typename... Ts> struct call_guard; template <typename... Ts>
struct call_guard;
template <> struct call_guard<> { using type = detail::void_type; }; template <>
struct call_guard<> {
using type = detail::void_type;
};
template <typename T> template <typename T>
struct call_guard<T> { struct call_guard<T> {
@ -123,8 +167,9 @@ PYBIND11_NAMESPACE_BEGIN(detail)
enum op_id : int; enum op_id : int;
enum op_type : int; enum op_type : int;
struct undefined_t; struct undefined_t;
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_; template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t>
inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); struct op_;
void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
/// Internal data structure which holds metadata about a keyword argument /// Internal data structure which holds metadata about a keyword argument
struct argument_record { struct argument_record {
@ -135,15 +180,16 @@ struct argument_record {
bool none : 1; ///< True if None is allowed when loading bool none : 1; ///< True if None is allowed when loading
argument_record(const char *name, const char *descr, handle value, bool convert, bool none) argument_record(const char *name, const char *descr, handle value, bool convert, bool none)
: name(name), descr(descr), value(value), convert(convert), none(none) { } : name(name), descr(descr), value(value), convert(convert), none(none) {}
}; };
/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) /// Internal data structure which holds metadata about a bound function (signature, overloads,
/// etc.)
struct function_record { struct function_record {
function_record() function_record()
: is_constructor(false), is_new_style_constructor(false), is_stateless(false), : is_constructor(false), is_new_style_constructor(false), is_stateless(false),
is_operator(false), is_method(false), has_args(false), is_operator(false), is_method(false), has_args(false), has_kwargs(false),
has_kwargs(false), has_kw_only_args(false), prepend(false) { } prepend(false) {}
/// Function name /// Function name
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
@ -158,13 +204,13 @@ struct function_record {
std::vector<argument_record> args; std::vector<argument_record> args;
/// Pointer to lambda function which converts arguments and performs the actual call /// Pointer to lambda function which converts arguments and performs the actual call
handle (*impl) (function_call &) = nullptr; handle (*impl)(function_call &) = nullptr;
/// Storage for the wrapped function pointer and captured data, if any /// Storage for the wrapped function pointer and captured data, if any
void *data[3] = { }; void *data[3] = {};
/// Pointer to custom destructor for 'data' (if needed) /// Pointer to custom destructor for 'data' (if needed)
void (*free_data) (function_record *ptr) = nullptr; void (*free_data)(function_record *ptr) = nullptr;
/// Return value policy associated with this function /// Return value policy associated with this function
return_value_policy policy = return_value_policy::automatic; return_value_policy policy = return_value_policy::automatic;
@ -190,17 +236,15 @@ struct function_record {
/// True if the function has a '**kwargs' argument /// True if the function has a '**kwargs' argument
bool has_kwargs : 1; bool has_kwargs : 1;
/// True once a 'py::kw_only' is encountered (any following args are keyword-only)
bool has_kw_only_args : 1;
/// True if this function is to be inserted at the beginning of the overload resolution chain /// True if this function is to be inserted at the beginning of the overload resolution chain
bool prepend : 1; bool prepend : 1;
/// Number of arguments (including py::args and/or py::kwargs, if present) /// Number of arguments (including py::args and/or py::kwargs, if present)
std::uint16_t nargs; std::uint16_t nargs;
/// Number of trailing arguments (counted in `nargs`) that are keyword-only /// Number of leading positional arguments, which are terminated by a py::args or py::kwargs
std::uint16_t nargs_kw_only = 0; /// argument or by a py::kw_only annotation.
std::uint16_t nargs_pos = 0;
/// Number of leading arguments (counted in `nargs`) that are positional-only /// Number of leading arguments (counted in `nargs`) that are positional-only
std::uint16_t nargs_pos_only = 0; std::uint16_t nargs_pos_only = 0;
@ -222,7 +266,7 @@ struct function_record {
struct type_record { struct type_record {
PYBIND11_NOINLINE type_record() PYBIND11_NOINLINE type_record()
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false),
default_holder(true), module_local(false), is_final(false) { } default_holder(true), module_local(false), is_final(false) {}
/// Handle to the parent scope /// Handle to the parent scope
handle scope; handle scope;
@ -260,6 +304,9 @@ struct type_record {
/// Custom metaclass (optional) /// Custom metaclass (optional)
handle metaclass; handle metaclass;
/// Custom type setup.
custom_type_setup::callback custom_type_setup_callback;
/// Multiple inheritance marker /// Multiple inheritance marker
bool multiple_inheritance : 1; bool multiple_inheritance : 1;
@ -278,42 +325,43 @@ struct type_record {
/// Is the class inheritable from python classes? /// Is the class inheritable from python classes?
bool is_final : 1; bool is_final : 1;
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) {
auto base_info = detail::get_type_info(base, false); auto *base_info = detail::get_type_info(base, false);
if (!base_info) { if (!base_info) {
std::string tname(base.name()); std::string tname(base.name());
detail::clean_type_id(tname); detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(name) + pybind11_fail("generic_type: type \"" + std::string(name)
"\" referenced unknown base type \"" + tname + "\""); + "\" referenced unknown base type \"" + tname + "\"");
} }
if (default_holder != base_info->default_holder) { if (default_holder != base_info->default_holder) {
std::string tname(base.name()); std::string tname(base.name());
detail::clean_type_id(tname); detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + pybind11_fail("generic_type: type \"" + std::string(name) + "\" "
(default_holder ? "does not have" : "has") + + (default_holder ? "does not have" : "has")
" a non-default holder type while its base \"" + tname + "\" " + + " a non-default holder type while its base \"" + tname + "\" "
(base_info->default_holder ? "does not" : "does")); + (base_info->default_holder ? "does not" : "does"));
} }
bases.append((PyObject *) base_info->type); bases.append((PyObject *) base_info->type);
if (base_info->type->tp_dictoffset != 0) if (base_info->type->tp_dictoffset != 0) {
dynamic_attr = true; dynamic_attr = true;
}
if (caster) if (caster) {
base_info->implicit_casts.emplace_back(type, caster); base_info->implicit_casts.emplace_back(type, caster);
}
} }
}; };
inline function_call::function_call(const function_record &f, handle p) : inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) {
func(f), parent(p) {
args.reserve(f.nargs); args.reserve(f.nargs);
args_convert.reserve(f.nargs); args_convert.reserve(f.nargs);
} }
/// Tag for a new-style `__init__` defined in `detail/init.h` /// Tag for a new-style `__init__` defined in `detail/init.h`
struct is_new_style_constructor { }; struct is_new_style_constructor {};
/** /**
* Partial template specializations to process custom attributes provided to * Partial template specializations to process custom attributes provided to
@ -321,100 +369,133 @@ struct is_new_style_constructor { };
* fields in the type_record and function_record data structures or executed at * fields in the type_record and function_record data structures or executed at
* runtime to deal with custom call policies (e.g. keep_alive). * runtime to deal with custom call policies (e.g. keep_alive).
*/ */
template <typename T, typename SFINAE = void> struct process_attribute; template <typename T, typename SFINAE = void>
struct process_attribute;
template <typename T> struct process_attribute_default { template <typename T>
struct process_attribute_default {
/// Default implementation: do nothing /// Default implementation: do nothing
static void init(const T &, function_record *) { } static void init(const T &, function_record *) {}
static void init(const T &, type_record *) { } static void init(const T &, type_record *) {}
static void precall(function_call &) { } static void precall(function_call &) {}
static void postcall(function_call &, handle) { } static void postcall(function_call &, handle) {}
}; };
/// Process an attribute specifying the function's name /// Process an attribute specifying the function's name
template <> struct process_attribute<name> : process_attribute_default<name> { template <>
struct process_attribute<name> : process_attribute_default<name> {
static void init(const name &n, function_record *r) { r->name = const_cast<char *>(n.value); } static void init(const name &n, function_record *r) { r->name = const_cast<char *>(n.value); }
}; };
/// Process an attribute specifying the function's docstring /// Process an attribute specifying the function's docstring
template <> struct process_attribute<doc> : process_attribute_default<doc> { template <>
struct process_attribute<doc> : process_attribute_default<doc> {
static void init(const doc &n, function_record *r) { r->doc = const_cast<char *>(n.value); } static void init(const doc &n, function_record *r) { r->doc = const_cast<char *>(n.value); }
}; };
/// Process an attribute specifying the function's docstring (provided as a C-style string) /// Process an attribute specifying the function's docstring (provided as a C-style string)
template <> struct process_attribute<const char *> : process_attribute_default<const char *> { template <>
struct process_attribute<const char *> : process_attribute_default<const char *> {
static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); } static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); }
static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); } static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); }
}; };
template <> struct process_attribute<char *> : process_attribute<const char *> { }; template <>
struct process_attribute<char *> : process_attribute<const char *> {};
/// Process an attribute indicating the function's return value policy /// Process an attribute indicating the function's return value policy
template <> struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> { template <>
struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> {
static void init(const return_value_policy &p, function_record *r) { r->policy = p; } static void init(const return_value_policy &p, function_record *r) { r->policy = p; }
}; };
/// Process an attribute which indicates that this is an overloaded function associated with a given sibling /// Process an attribute which indicates that this is an overloaded function associated with a
template <> struct process_attribute<sibling> : process_attribute_default<sibling> { /// given sibling
template <>
struct process_attribute<sibling> : process_attribute_default<sibling> {
static void init(const sibling &s, function_record *r) { r->sibling = s.value; } static void init(const sibling &s, function_record *r) { r->sibling = s.value; }
}; };
/// Process an attribute which indicates that this function is a method /// Process an attribute which indicates that this function is a method
template <> struct process_attribute<is_method> : process_attribute_default<is_method> { template <>
static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } struct process_attribute<is_method> : process_attribute_default<is_method> {
static void init(const is_method &s, function_record *r) {
r->is_method = true;
r->scope = s.class_;
}
}; };
/// Process an attribute which indicates the parent scope of a method /// Process an attribute which indicates the parent scope of a method
template <> struct process_attribute<scope> : process_attribute_default<scope> { template <>
struct process_attribute<scope> : process_attribute_default<scope> {
static void init(const scope &s, function_record *r) { r->scope = s.value; } static void init(const scope &s, function_record *r) { r->scope = s.value; }
}; };
/// Process an attribute which indicates that this function is an operator /// Process an attribute which indicates that this function is an operator
template <> struct process_attribute<is_operator> : process_attribute_default<is_operator> { template <>
struct process_attribute<is_operator> : process_attribute_default<is_operator> {
static void init(const is_operator &, function_record *r) { r->is_operator = true; } static void init(const is_operator &, function_record *r) { r->is_operator = true; }
}; };
template <> struct process_attribute<is_new_style_constructor> : process_attribute_default<is_new_style_constructor> { template <>
static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } struct process_attribute<is_new_style_constructor>
: process_attribute_default<is_new_style_constructor> {
static void init(const is_new_style_constructor &, function_record *r) {
r->is_new_style_constructor = true;
}
}; };
inline void process_kw_only_arg(const arg &a, function_record *r) { inline void check_kw_only_arg(const arg &a, function_record *r) {
if (!a.name || a.name[0] == '\0') if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) {
pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation"); pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or "
++r->nargs_kw_only; "args() argument");
}
}
inline void append_self_arg_if_needed(function_record *r) {
if (r->is_method && r->args.empty()) {
r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false);
}
} }
/// Process a keyword argument attribute (*without* a default value) /// Process a keyword argument attribute (*without* a default value)
template <> struct process_attribute<arg> : process_attribute_default<arg> { template <>
struct process_attribute<arg> : process_attribute_default<arg> {
static void init(const arg &a, function_record *r) { static void init(const arg &a, function_record *r) {
if (r->is_method && r->args.empty()) append_self_arg_if_needed(r);
r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/);
r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none);
if (r->has_kw_only_args) process_kw_only_arg(a, r); check_kw_only_arg(a, r);
} }
}; };
/// Process a keyword argument attribute (*with* a default value) /// Process a keyword argument attribute (*with* a default value)
template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> { template <>
struct process_attribute<arg_v> : process_attribute_default<arg_v> {
static void init(const arg_v &a, function_record *r) { static void init(const arg_v &a, function_record *r) {
if (r->is_method && r->args.empty()) if (r->is_method && r->args.empty()) {
r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); r->args.emplace_back(
"self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false);
}
if (!a.value) { if (!a.value) {
#if !defined(NDEBUG) #if !defined(NDEBUG)
std::string descr("'"); std::string descr("'");
if (a.name) descr += std::string(a.name) + ": "; if (a.name) {
descr += std::string(a.name) + ": ";
}
descr += a.type + "'"; descr += a.type + "'";
if (r->is_method) { if (r->is_method) {
if (r->name) if (r->name) {
descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; descr += " in method '" + (std::string) str(r->scope) + "."
else + (std::string) r->name + "'";
} else {
descr += " in method of '" + (std::string) str(r->scope) + "'"; descr += " in method of '" + (std::string) str(r->scope) + "'";
}
} else if (r->name) { } else if (r->name) {
descr += " in function '" + (std::string) r->name + "'"; descr += " in function '" + (std::string) r->name + "'";
} }
pybind11_fail("arg(): could not convert default argument " pybind11_fail("arg(): could not convert default argument " + descr
+ descr + " into a Python object (type not registered yet?)"); + " into a Python object (type not registered yet?)");
#else #else
pybind11_fail("arg(): could not convert default argument " pybind11_fail("arg(): could not convert default argument "
"into a Python object (type not registered yet?). " "into a Python object (type not registered yet?). "
@ -423,27 +504,41 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
} }
r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none);
if (r->has_kw_only_args) process_kw_only_arg(a, r); check_kw_only_arg(a, r);
} }
}; };
/// Process a keyword-only-arguments-follow pseudo argument /// Process a keyword-only-arguments-follow pseudo argument
template <> struct process_attribute<kw_only> : process_attribute_default<kw_only> { template <>
struct process_attribute<kw_only> : process_attribute_default<kw_only> {
static void init(const kw_only &, function_record *r) { static void init(const kw_only &, function_record *r) {
r->has_kw_only_args = true; append_self_arg_if_needed(r);
if (r->has_args && r->nargs_pos != static_cast<std::uint16_t>(r->args.size())) {
pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative "
"argument location (or omit kw_only() entirely)");
}
r->nargs_pos = static_cast<std::uint16_t>(r->args.size());
} }
}; };
/// Process a positional-only-argument maker /// Process a positional-only-argument maker
template <> struct process_attribute<pos_only> : process_attribute_default<pos_only> { template <>
struct process_attribute<pos_only> : process_attribute_default<pos_only> {
static void init(const pos_only &, function_record *r) { static void init(const pos_only &, function_record *r) {
append_self_arg_if_needed(r);
r->nargs_pos_only = static_cast<std::uint16_t>(r->args.size()); r->nargs_pos_only = static_cast<std::uint16_t>(r->args.size());
if (r->nargs_pos_only > r->nargs_pos) {
pybind11_fail("pos_only(): cannot follow a py::args() argument");
}
// It also can't follow a kw_only, but a static_assert in pybind11.h checks that
} }
}; };
/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) /// Process a parent class attribute. Single inheritance only (class_ itself already guarantees
/// that)
template <typename T> template <typename T>
struct process_attribute<T, enable_if_t<is_pyobject<T>::value>> : process_attribute_default<handle> { struct process_attribute<T, enable_if_t<is_pyobject<T>::value>>
: process_attribute_default<handle> {
static void init(const handle &h, type_record *r) { r->bases.append(h); } static void init(const handle &h, type_record *r) { r->bases.append(h); }
}; };
@ -456,7 +551,9 @@ struct process_attribute<base<T>> : process_attribute_default<base<T>> {
/// Process a multiple inheritance attribute /// Process a multiple inheritance attribute
template <> template <>
struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> { struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> {
static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } static void init(const multiple_inheritance &, type_record *r) {
r->multiple_inheritance = true;
}
}; };
template <> template <>
@ -464,6 +561,13 @@ struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr>
static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; }
}; };
template <>
struct process_attribute<custom_type_setup> {
static void init(const custom_type_setup &value, type_record *r) {
r->custom_type_setup_callback = value.value;
}
};
template <> template <>
struct process_attribute<is_final> : process_attribute_default<is_final> { struct process_attribute<is_final> : process_attribute_default<is_final> {
static void init(const is_final &, type_record *r) { r->is_final = true; } static void init(const is_final &, type_record *r) { r->is_final = true; }
@ -495,41 +599,59 @@ template <>
struct process_attribute<arithmetic> : process_attribute_default<arithmetic> {}; struct process_attribute<arithmetic> : process_attribute_default<arithmetic> {};
template <typename... Ts> template <typename... Ts>
struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_guard<Ts...>> { }; struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_guard<Ts...>> {};
/** /**
* Process a keep_alive call policy -- invokes keep_alive_impl during the * Process a keep_alive call policy -- invokes keep_alive_impl during the
* pre-call handler if both Nurse, Patient != 0 and use the post-call handler * pre-call handler if both Nurse, Patient != 0 and use the post-call handler
* otherwise * otherwise
*/ */
template <size_t Nurse, size_t Patient> struct process_attribute<keep_alive<Nurse, Patient>> : public process_attribute_default<keep_alive<Nurse, Patient>> { template <size_t Nurse, size_t Patient>
struct process_attribute<keep_alive<Nurse, Patient>>
: public process_attribute_default<keep_alive<Nurse, Patient>> {
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0> template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } static void precall(function_call &call) {
keep_alive_impl(Nurse, Patient, call, handle());
}
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0> template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
static void postcall(function_call &, handle) { } static void postcall(function_call &, handle) {}
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0> template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
static void precall(function_call &) { } static void precall(function_call &) {}
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0> template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } static void postcall(function_call &call, handle ret) {
keep_alive_impl(Nurse, Patient, call, ret);
}
}; };
/// Recursively iterate over variadic template arguments /// Recursively iterate over variadic template arguments
template <typename... Args> struct process_attributes { template <typename... Args>
static void init(const Args&... args, function_record *r) { struct process_attributes {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... }; static void init(const Args &...args, function_record *r) {
ignore_unused(unused); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
using expander = int[];
(void) expander{
0, ((void) process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
} }
static void init(const Args&... args, type_record *r) { static void init(const Args &...args, type_record *r) {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... }; PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
ignore_unused(unused); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
using expander = int[];
(void) expander{0,
(process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
} }
static void precall(function_call &call) { static void precall(function_call &call) {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::precall(call), 0) ... }; PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call);
ignore_unused(unused); using expander = int[];
(void) expander{0,
(process_attribute<typename std::decay<Args>::type>::precall(call), 0)...};
} }
static void postcall(function_call &call, handle fn_ret) { static void postcall(function_call &call, handle fn_ret) {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0) ... }; PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret);
ignore_unused(unused); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret);
using expander = int[];
(void) expander{
0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0)...};
} }
}; };
@ -543,8 +665,9 @@ using extract_guard_t = typename exactly_one_t<is_call_guard, call_guard<>, Extr
/// Check the number of named arguments at compile time /// Check the number of named arguments at compile time
template <typename... Extra, template <typename... Extra,
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...), size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)> size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs);
return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs;
} }

View File

@ -19,9 +19,11 @@ PYBIND11_NAMESPACE_BEGIN(detail)
inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) { inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
auto ndim = shape.size(); auto ndim = shape.size();
std::vector<ssize_t> strides(ndim, itemsize); std::vector<ssize_t> strides(ndim, itemsize);
if (ndim > 0) if (ndim > 0) {
for (size_t i = ndim - 1; i > 0; --i) for (size_t i = ndim - 1; i > 0; --i) {
strides[i - 1] = strides[i] * shape[i]; strides[i - 1] = strides[i] * shape[i];
}
}
return strides; return strides;
} }
@ -29,8 +31,9 @@ inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t
inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) { inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
auto ndim = shape.size(); auto ndim = shape.size();
std::vector<ssize_t> strides(ndim, itemsize); std::vector<ssize_t> strides(ndim, itemsize);
for (size_t i = 1; i < ndim; ++i) for (size_t i = 1; i < ndim; ++i) {
strides[i] = strides[i - 1] * shape[i - 1]; strides[i] = strides[i - 1] * shape[i - 1];
}
return strides; return strides;
} }
@ -41,55 +44,85 @@ struct buffer_info {
void *ptr = nullptr; // Pointer to the underlying storage void *ptr = nullptr; // Pointer to the underlying storage
ssize_t itemsize = 0; // Size of individual items in bytes ssize_t itemsize = 0; // Size of individual items in bytes
ssize_t size = 0; // Total number of entries ssize_t size = 0; // Total number of entries
std::string format; // For homogeneous buffers, this should be set to format_descriptor<T>::format() std::string format; // For homogeneous buffers, this should be set to
// format_descriptor<T>::format()
ssize_t ndim = 0; // Number of dimensions ssize_t ndim = 0; // Number of dimensions
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension) std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
std::vector<ssize_t> strides; // Number of bytes between adjacent entries (for each per dimension) std::vector<ssize_t> strides; // Number of bytes between adjacent entries
// (for each per dimension)
bool readonly = false; // flag to indicate if the underlying storage may be written to bool readonly = false; // flag to indicate if the underlying storage may be written to
buffer_info() = default; buffer_info() = default;
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, buffer_info(void *ptr,
detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false) ssize_t itemsize,
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), const std::string &format,
shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { ssize_t ndim,
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) detail::any_container<ssize_t> shape_in,
detail::any_container<ssize_t> strides_in,
bool readonly = false)
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) {
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
for (size_t i = 0; i < (size_t) ndim; ++i) }
for (size_t i = 0; i < (size_t) ndim; ++i) {
size *= shape[i]; size *= shape[i];
}
} }
template <typename T> template <typename T>
buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false) buffer_info(T *ptr,
: buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { } detail::any_container<ssize_t> shape_in,
detail::any_container<ssize_t> strides_in,
bool readonly = false)
: buffer_info(private_ctr_tag(),
ptr,
sizeof(T),
format_descriptor<T>::format(),
static_cast<ssize_t>(shape_in->size()),
std::move(shape_in),
std::move(strides_in),
readonly) {}
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false) buffer_info(void *ptr,
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { } ssize_t itemsize,
const std::string &format,
ssize_t size,
bool readonly = false)
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {}
template <typename T> template <typename T>
buffer_info(T *ptr, ssize_t size, bool readonly=false) buffer_info(T *ptr, ssize_t size, bool readonly = false)
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) { } : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) {}
template <typename T> template <typename T>
buffer_info(const T *ptr, ssize_t size, bool readonly=true) buffer_info(const T *ptr, ssize_t size, bool readonly = true)
: buffer_info(const_cast<T*>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) { } : buffer_info(
const_cast<T *>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) {}
explicit buffer_info(Py_buffer *view, bool ownview = true) explicit buffer_info(Py_buffer *view, bool ownview = true)
: buffer_info(view->buf, view->itemsize, view->format, view->ndim, : buffer_info(
view->buf,
view->itemsize,
view->format,
view->ndim,
{view->shape, view->shape + view->ndim}, {view->shape, view->shape + view->ndim},
/* Though buffer::request() requests PyBUF_STRIDES, ctypes objects /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects
* ignore this flag and return a view with NULL strides. * ignore this flag and return a view with NULL strides.
* When strides are NULL, build them manually. */ * When strides are NULL, build them manually. */
view->strides view->strides
? std::vector<ssize_t>(view->strides, view->strides + view->ndim) ? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
(view->readonly != 0)) { (view->readonly != 0)) {
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
this->m_view = view; this->m_view = view;
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
this->ownview = ownview; this->ownview = ownview;
} }
buffer_info(const buffer_info &) = delete; buffer_info(const buffer_info &) = delete;
buffer_info& operator=(const buffer_info &) = delete; buffer_info &operator=(const buffer_info &) = delete;
buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }
@ -108,17 +141,28 @@ struct buffer_info {
} }
~buffer_info() { ~buffer_info() {
if (m_view && ownview) { PyBuffer_Release(m_view); delete m_view; } if (m_view && ownview) {
PyBuffer_Release(m_view);
delete m_view;
}
} }
Py_buffer *view() const { return m_view; } Py_buffer *view() const { return m_view; }
Py_buffer *&view() { return m_view; } Py_buffer *&view() { return m_view; }
private:
struct private_ctr_tag { };
buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, private:
detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in, bool readonly) struct private_ctr_tag {};
: buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { }
buffer_info(private_ctr_tag,
void *ptr,
ssize_t itemsize,
const std::string &format,
ssize_t ndim,
detail::any_container<ssize_t> &&shape_in,
detail::any_container<ssize_t> &&strides_in,
bool readonly)
: buffer_info(
ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {}
Py_buffer *m_view = nullptr; Py_buffer *m_view = nullptr;
bool ownview = false; bool ownview = false;
@ -126,17 +170,22 @@ private:
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <typename T, typename SFINAE = void> struct compare_buffer_info { template <typename T, typename SFINAE = void>
static bool compare(const buffer_info& b) { struct compare_buffer_info {
static bool compare(const buffer_info &b) {
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T); return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
} }
}; };
template <typename T> struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> { template <typename T>
static bool compare(const buffer_info& b) { struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor<T>::value || static bool compare(const buffer_info &b) {
((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned<T>::value ? "L" : "l")) || return (size_t) b.itemsize == sizeof(T)
((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned<T>::value ? "N" : "n"))); && (b.format == format_descriptor<T>::value
|| ((sizeof(T) == sizeof(long))
&& b.format == (std::is_unsigned<T>::value ? "L" : "l"))
|| ((sizeof(T) == sizeof(size_t))
&& b.format == (std::is_unsigned<T>::value ? "N" : "n")));
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@ -15,63 +15,70 @@
#include <chrono> #include <chrono>
#include <cmath> #include <cmath>
#include <ctime> #include <ctime>
#include <mutex>
#include <time.h>
#include <datetime.h> #include <datetime.h>
#include <mutex>
// Backport the PyDateTime_DELTA functions from Python3.3 if required // Backport the PyDateTime_DELTA functions from Python3.3 if required
#ifndef PyDateTime_DELTA_GET_DAYS #ifndef PyDateTime_DELTA_GET_DAYS
#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) # define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta *) o)->days)
#endif #endif
#ifndef PyDateTime_DELTA_GET_SECONDS #ifndef PyDateTime_DELTA_GET_SECONDS
#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) # define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta *) o)->seconds)
#endif #endif
#ifndef PyDateTime_DELTA_GET_MICROSECONDS #ifndef PyDateTime_DELTA_GET_MICROSECONDS
#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) # define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta *) o)->microseconds)
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <typename type> class duration_caster { template <typename type>
class duration_caster {
public: public:
using rep = typename type::rep; using rep = typename type::rep;
using period = typename type::period; using period = typename type::period;
using days = std::chrono::duration<int_least32_t, std::ratio<86400>>; // signed 25 bits required by the standard. // signed 25 bits required by the standard.
using days = std::chrono::duration<int_least32_t, std::ratio<86400>>;
bool load(handle src, bool) { bool load(handle src, bool) {
using namespace std::chrono; using namespace std::chrono;
// Lazy initialise the PyDateTime import // Lazy initialise the PyDateTime import
if (!PyDateTimeAPI) { PyDateTime_IMPORT; } if (!PyDateTimeAPI) {
PyDateTime_IMPORT;
}
if (!src) return false; if (!src) {
return false;
}
// If invoked with datetime.delta object // If invoked with datetime.delta object
if (PyDelta_Check(src.ptr())) { if (PyDelta_Check(src.ptr())) {
value = type(duration_cast<duration<rep, period>>( value = type(duration_cast<duration<rep, period>>(
days(PyDateTime_DELTA_GET_DAYS(src.ptr())) days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
+ seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
+ microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
return true; return true;
} }
// If invoked with a float we assume it is seconds and convert // If invoked with a float we assume it is seconds and convert
if (PyFloat_Check(src.ptr())) { if (PyFloat_Check(src.ptr())) {
value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr())))); value = type(duration_cast<duration<rep, period>>(
duration<double>(PyFloat_AsDouble(src.ptr()))));
return true; return true;
} }
return false; return false;
} }
// If this is a duration just return it back // If this is a duration just return it back
static const std::chrono::duration<rep, period>& get_duration(const std::chrono::duration<rep, period> &src) { static const std::chrono::duration<rep, period> &
get_duration(const std::chrono::duration<rep, period> &src) {
return src; return src;
} }
// If this is a time_point get the time_since_epoch // If this is a time_point get the time_since_epoch
template <typename Clock> static std::chrono::duration<rep, period> get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) { template <typename Clock>
static std::chrono::duration<rep, period>
get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) {
return src.time_since_epoch(); return src.time_since_epoch();
} }
@ -83,9 +90,12 @@ public:
auto d = get_duration(src); auto d = get_duration(src);
// Lazy initialise the PyDateTime import // Lazy initialise the PyDateTime import
if (!PyDateTimeAPI) { PyDateTime_IMPORT; } if (!PyDateTimeAPI) {
PyDateTime_IMPORT;
}
// Declare these special duration types so the conversions happen with the correct primitive types (int) // Declare these special duration types so the conversions happen with the correct
// primitive types (int)
using dd_t = duration<int, std::ratio<86400>>; using dd_t = duration<int, std::ratio<86400>>;
using ss_t = duration<int, std::ratio<1>>; using ss_t = duration<int, std::ratio<1>>;
using us_t = duration<int, std::micro>; using us_t = duration<int, std::micro>;
@ -97,7 +107,7 @@ public:
return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); return PyDelta_FromDSU(dd.count(), ss.count(), us.count());
} }
PYBIND11_TYPE_CASTER(type, _x("datetime.timedelta")); PYBIND11_TYPE_CASTER(type, const_name("datetime.timedelta"));
}; };
inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) { inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
@ -108,7 +118,7 @@ inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
#else #else
static std::mutex mtx; static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx); std::lock_guard<std::mutex> lock(mtx);
std::tm *tm_ptr = localtime(time); std::tm *tm_ptr = std::localtime(time);
if (tm_ptr != nullptr) { if (tm_ptr != nullptr) {
*buf = *tm_ptr; *buf = *tm_ptr;
} }
@ -117,76 +127,89 @@ inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
} }
// This is for casting times on the system clock into datetime.datetime instances // This is for casting times on the system clock into datetime.datetime instances
template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> { template <typename Duration>
class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
public: public:
using type = std::chrono::time_point<std::chrono::system_clock, Duration>; using type = std::chrono::time_point<std::chrono::system_clock, Duration>;
bool load(handle src, bool) { bool load(handle src, bool) {
using namespace std::chrono; using namespace std::chrono;
// Lazy initialise the PyDateTime import // Lazy initialise the PyDateTime import
if (!PyDateTimeAPI) { PyDateTime_IMPORT; } if (!PyDateTimeAPI) {
PyDateTime_IMPORT;
}
if (!src) return false; if (!src) {
return false;
}
std::tm cal; std::tm cal;
microseconds msecs; microseconds msecs;
if (PyDateTime_Check(src.ptr())) { if (PyDateTime_Check(src.ptr())) {
cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
cal.tm_isdst = -1; cal.tm_isdst = -1;
msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
} else if (PyDate_Check(src.ptr())) { } else if (PyDate_Check(src.ptr())) {
cal.tm_sec = 0; cal.tm_sec = 0;
cal.tm_min = 0; cal.tm_min = 0;
cal.tm_hour = 0; cal.tm_hour = 0;
cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
cal.tm_isdst = -1; cal.tm_isdst = -1;
msecs = microseconds(0); msecs = microseconds(0);
} else if (PyTime_Check(src.ptr())) { } else if (PyTime_Check(src.ptr())) {
cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr()); cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr()); cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr()); cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70) cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
cal.tm_mon = 0; // represents 1-Jan-1970, which is the first cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
cal.tm_year = 70; // earliest available date for Python's datetime cal.tm_year = 70; // earliest available date for Python's datetime
cal.tm_isdst = -1; cal.tm_isdst = -1;
msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr())); msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
} else {
return false;
} }
else return false;
value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs); value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs);
return true; return true;
} }
static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, return_value_policy /* policy */, handle /* parent */) { static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src,
return_value_policy /* policy */,
handle /* parent */) {
using namespace std::chrono; using namespace std::chrono;
// Lazy initialise the PyDateTime import // Lazy initialise the PyDateTime import
if (!PyDateTimeAPI) { PyDateTime_IMPORT; } if (!PyDateTimeAPI) {
PyDateTime_IMPORT;
}
// Get out microseconds, and make sure they are positive, to avoid bug in eastern hemisphere time zones // Get out microseconds, and make sure they are positive, to avoid bug in eastern
// (cfr. https://github.com/pybind/pybind11/issues/2417) // hemisphere time zones (cfr. https://github.com/pybind/pybind11/issues/2417)
using us_t = duration<int, std::micro>; using us_t = duration<int, std::micro>;
auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1)); auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1));
if (us.count() < 0) if (us.count() < 0) {
us += seconds(1); us += seconds(1);
}
// Subtract microseconds BEFORE `system_clock::to_time_t`, because: // Subtract microseconds BEFORE `system_clock::to_time_t`, because:
// > If std::time_t has lower precision, it is implementation-defined whether the value is rounded or truncated. // > If std::time_t has lower precision, it is implementation-defined whether the value is
// (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t) // rounded or truncated. (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us)); std::time_t tt
= system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
std::tm localtime; std::tm localtime;
std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime); std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
if (!localtime_ptr) if (!localtime_ptr) {
throw cast_error("Unable to represent system_clock in local time"); throw cast_error("Unable to represent system_clock in local time");
}
return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
localtime.tm_mon + 1, localtime.tm_mon + 1,
localtime.tm_mday, localtime.tm_mday,
@ -195,19 +218,19 @@ public:
localtime.tm_sec, localtime.tm_sec,
us.count()); us.count());
} }
PYBIND11_TYPE_CASTER(type, _x("datetime.datetime")); PYBIND11_TYPE_CASTER(type, const_name("datetime.datetime"));
}; };
// Other clocks that are not the system clock are not measured as datetime.datetime objects // Other clocks that are not the system clock are not measured as datetime.datetime objects
// since they are not measured on calendar time. So instead we just make them timedeltas // since they are not measured on calendar time. So instead we just make them timedeltas
// Or if they have passed us a time as a float we convert that // Or if they have passed us a time as a float we convert that
template <typename Clock, typename Duration> class type_caster<std::chrono::time_point<Clock, Duration>> template <typename Clock, typename Duration>
: public duration_caster<std::chrono::time_point<Clock, Duration>> { class type_caster<std::chrono::time_point<Clock, Duration>>
}; : public duration_caster<std::chrono::time_point<Clock, Duration>> {};
template <typename Rep, typename Period> class type_caster<std::chrono::duration<Rep, Period>> template <typename Rep, typename Period>
: public duration_caster<std::chrono::duration<Rep, Period>> { class type_caster<std::chrono::duration<Rep, Period>>
}; : public duration_caster<std::chrono::duration<Rep, Period>> {};
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -10,42 +10,50 @@
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include <complex> #include <complex>
/// glibc defines I as a macro which breaks things, e.g., boost template names /// glibc defines I as a macro which breaks things, e.g., boost template names
#ifdef I #ifdef I
# undef I # undef I
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
template <typename T> struct format_descriptor<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> { template <typename T>
struct format_descriptor<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
static constexpr const char c = format_descriptor<T>::c; static constexpr const char c = format_descriptor<T>::c;
static constexpr const char value[3] = { 'Z', c, '\0' }; static constexpr const char value[3] = {'Z', c, '\0'};
static std::string format() { return std::string(value); } static std::string format() { return std::string(value); }
}; };
#ifndef PYBIND11_CPP17 #ifndef PYBIND11_CPP17
template <typename T> constexpr const char format_descriptor< template <typename T>
std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>>::value[3]; constexpr const char
format_descriptor<std::complex<T>,
detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
#endif #endif
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <typename T> struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> { template <typename T>
struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
static constexpr bool value = true; static constexpr bool value = true;
static constexpr int index = is_fmt_numeric<T>::index + 3; static constexpr int index = is_fmt_numeric<T>::index + 3;
}; };
template <typename T> class type_caster<std::complex<T>> { template <typename T>
class type_caster<std::complex<T>> {
public: public:
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!src) if (!src) {
return false; return false;
if (!convert && !PyComplex_Check(src.ptr())) }
if (!convert && !PyComplex_Check(src.ptr())) {
return false; return false;
}
Py_complex result = PyComplex_AsCComplex(src.ptr()); Py_complex result = PyComplex_AsCComplex(src.ptr());
if (result.real == -1.0 && PyErr_Occurred()) { if (result.real == -1.0 && PyErr_Occurred()) {
PyErr_Clear(); PyErr_Clear();
@ -55,11 +63,12 @@ public:
return true; return true;
} }
static handle cast(const std::complex<T> &src, return_value_policy /* policy */, handle /* parent */) { static handle
cast(const std::complex<T> &src, return_value_policy /* policy */, handle /* parent */) {
return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); return PyComplex_FromDoubles((double) src.real(), (double) src.imag());
} }
PYBIND11_TYPE_CASTER(std::complex<T>, _x("complex")); PYBIND11_TYPE_CASTER(std::complex<T>, const_name("complex"));
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -16,12 +16,13 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION) #if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION)
# define PYBIND11_BUILTIN_QUALNAME # define PYBIND11_BUILTIN_QUALNAME
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
#else #else
// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type // In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type
// signatures; in 3.3+ this macro expands to nothing: // signatures; in 3.3+ this macro expands to nothing:
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj) # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) \
setattr((PyObject *) obj, "__qualname__", nameobj)
#endif #endif
inline std::string get_fully_qualified_tp_name(PyTypeObject *type) { inline std::string get_fully_qualified_tp_name(PyTypeObject *type) {
@ -65,24 +66,26 @@ inline PyTypeObject *make_static_property_type() {
issue no Python C API calls which could potentially invoke the issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */ turn find the newly constructed type in an invalid state) */
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
if (!heap_type) if (!heap_type) {
pybind11_fail("make_static_property_type(): error allocating type!"); pybind11_fail("make_static_property_type(): error allocating type!");
}
heap_type->ht_name = name_obj.inc_ref().ptr(); heap_type->ht_name = name_obj.inc_ref().ptr();
#ifdef PYBIND11_BUILTIN_QUALNAME # ifdef PYBIND11_BUILTIN_QUALNAME
heap_type->ht_qualname = name_obj.inc_ref().ptr(); heap_type->ht_qualname = name_obj.inc_ref().ptr();
#endif # endif
auto type = &heap_type->ht_type; auto *type = &heap_type->ht_type;
type->tp_name = name; type->tp_name = name;
type->tp_base = type_incref(&PyProperty_Type); type->tp_base = type_incref(&PyProperty_Type);
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
type->tp_descr_get = pybind11_static_get; type->tp_descr_get = pybind11_static_get;
type->tp_descr_set = pybind11_static_set; type->tp_descr_set = pybind11_static_set;
if (PyType_Ready(type) < 0) if (PyType_Ready(type) < 0) {
pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
}
setattr((PyObject *) type, "__module__", str("pybind11_builtins")); setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
@ -98,15 +101,17 @@ inline PyTypeObject *make_static_property_type() {
inline PyTypeObject *make_static_property_type() { inline PyTypeObject *make_static_property_type() {
auto d = dict(); auto d = dict();
PyObject *result = PyRun_String(R"(\ PyObject *result = PyRun_String(R"(\
class pybind11_static_property(property): class pybind11_static_property(property):
def __get__(self, obj, cls): def __get__(self, obj, cls):
return property.__get__(self, cls, cls) return property.__get__(self, cls, cls)
def __set__(self, obj, value): def __set__(self, obj, value):
cls = obj if isinstance(obj, type) else type(obj) cls = obj if isinstance(obj, type) else type(obj)
property.__set__(self, cls, value) property.__set__(self, cls, value)
)", Py_file_input, d.ptr(), d.ptr() )",
); Py_file_input,
d.ptr(),
d.ptr());
if (result == nullptr) if (result == nullptr)
throw error_already_set(); throw error_already_set();
Py_DECREF(result); Py_DECREF(result);
@ -119,7 +124,7 @@ inline PyTypeObject *make_static_property_type() {
By default, Python replaces the `static_property` itself, but for wrapped C++ types By default, Python replaces the `static_property` itself, but for wrapped C++ types
we need to call `static_property.__set__()` in order to propagate the new value to we need to call `static_property.__set__()` in order to propagate the new value to
the underlying C++ data structure. */ the underlying C++ data structure. */
extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyObject *value) {
// Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
@ -128,7 +133,7 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb
// 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)`
// 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop`
// 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment
const auto static_prop = (PyObject *) get_internals().static_property_type; auto *const static_prop = (PyObject *) get_internals().static_property_type;
const auto call_descr_set = (descr != nullptr) && (value != nullptr) const auto call_descr_set = (descr != nullptr) && (value != nullptr)
&& (PyObject_IsInstance(descr, static_prop) != 0) && (PyObject_IsInstance(descr, static_prop) != 0)
&& (PyObject_IsInstance(value, static_prop) == 0); && (PyObject_IsInstance(value, static_prop) == 0);
@ -177,12 +182,13 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
} }
// This must be a pybind11 instance // This must be a pybind11 instance
auto instance = reinterpret_cast<detail::instance *>(self); auto *instance = reinterpret_cast<detail::instance *>(self);
// Ensure that the base __init__ function(s) were called // Ensure that the base __init__ function(s) were called
for (const auto &vh : values_and_holders(instance)) { for (const auto &vh : values_and_holders(instance)) {
if (!vh.holder_constructed()) { if (!vh.holder_constructed()) {
PyErr_Format(PyExc_TypeError, "%.200s.__init__() must be called when overriding __init__", PyErr_Format(PyExc_TypeError,
"%.200s.__init__() must be called when overriding __init__",
get_fully_qualified_tp_name(vh.type->type).c_str()); get_fully_qualified_tp_name(vh.type->type).c_str());
Py_DECREF(self); Py_DECREF(self);
return nullptr; return nullptr;
@ -201,27 +207,28 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
// 1) be found in internals.registered_types_py // 1) be found in internals.registered_types_py
// 2) have exactly one associated `detail::type_info` // 2) have exactly one associated `detail::type_info`
auto found_type = internals.registered_types_py.find(type); auto found_type = internals.registered_types_py.find(type);
if (found_type != internals.registered_types_py.end() && if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1
found_type->second.size() == 1 && && found_type->second[0]->type == type) {
found_type->second[0]->type == type) {
auto *tinfo = found_type->second[0]; auto *tinfo = found_type->second[0];
auto tindex = std::type_index(*tinfo->cpptype); auto tindex = std::type_index(*tinfo->cpptype);
internals.direct_conversions.erase(tindex); internals.direct_conversions.erase(tindex);
if (tinfo->module_local) if (tinfo->module_local) {
registered_local_types_cpp().erase(tindex); get_local_internals().registered_types_cpp.erase(tindex);
else } else {
internals.registered_types_cpp.erase(tindex); internals.registered_types_cpp.erase(tindex);
}
internals.registered_types_py.erase(tinfo->type); internals.registered_types_py.erase(tinfo->type);
// Actually just `std::erase_if`, but that's only available in C++20 // Actually just `std::erase_if`, but that's only available in C++20
auto &cache = internals.inactive_override_cache; auto &cache = internals.inactive_override_cache;
for (auto it = cache.begin(), last = cache.end(); it != last; ) { for (auto it = cache.begin(), last = cache.end(); it != last;) {
if (it->first == (PyObject *) tinfo->type) if (it->first == (PyObject *) tinfo->type) {
it = cache.erase(it); it = cache.erase(it);
else } else {
++it; ++it;
}
} }
delete tinfo; delete tinfo;
@ -233,7 +240,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
/** This metaclass is assigned by default to all pybind11 types and is required in order /** This metaclass is assigned by default to all pybind11 types and is required in order
for static properties to function correctly. Users may override this using `py::metaclass`. for static properties to function correctly. Users may override this using `py::metaclass`.
Return value: New reference. */ Return value: New reference. */
inline PyTypeObject* make_default_metaclass() { inline PyTypeObject *make_default_metaclass() {
constexpr auto *name = "pybind11_type"; constexpr auto *name = "pybind11_type";
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name)); auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
@ -241,16 +248,17 @@ inline PyTypeObject* make_default_metaclass() {
issue no Python C API calls which could potentially invoke the issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */ turn find the newly constructed type in an invalid state) */
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
if (!heap_type) if (!heap_type) {
pybind11_fail("make_default_metaclass(): error allocating metaclass!"); pybind11_fail("make_default_metaclass(): error allocating metaclass!");
}
heap_type->ht_name = name_obj.inc_ref().ptr(); heap_type->ht_name = name_obj.inc_ref().ptr();
#ifdef PYBIND11_BUILTIN_QUALNAME #ifdef PYBIND11_BUILTIN_QUALNAME
heap_type->ht_qualname = name_obj.inc_ref().ptr(); heap_type->ht_qualname = name_obj.inc_ref().ptr();
#endif #endif
auto type = &heap_type->ht_type; auto *type = &heap_type->ht_type;
type->tp_name = name; type->tp_name = name;
type->tp_base = type_incref(&PyType_Type); type->tp_base = type_incref(&PyType_Type);
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
@ -264,8 +272,9 @@ inline PyTypeObject* make_default_metaclass() {
type->tp_dealloc = pybind11_meta_dealloc; type->tp_dealloc = pybind11_meta_dealloc;
if (PyType_Ready(type) < 0) if (PyType_Ready(type) < 0) {
pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!");
}
setattr((PyObject *) type, "__module__", str("pybind11_builtins")); setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
@ -275,16 +284,20 @@ inline PyTypeObject* make_default_metaclass() {
/// For multiple inheritance types we need to recursively register/deregister base pointers for any /// For multiple inheritance types we need to recursively register/deregister base pointers for any
/// base classes with pointers that are difference from the instance value pointer so that we can /// base classes with pointers that are difference from the instance value pointer so that we can
/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. /// correctly recognize an offset base class pointer. This calls a function with any offset base
inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, /// ptrs.
bool (*f)(void * /*parentptr*/, instance * /*self*/)) { inline void traverse_offset_bases(void *valueptr,
const detail::type_info *tinfo,
instance *self,
bool (*f)(void * /*parentptr*/, instance * /*self*/)) {
for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) { for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) {
if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { if (auto *parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) {
for (auto &c : parent_tinfo->implicit_casts) { for (auto &c : parent_tinfo->implicit_casts) {
if (c.first == tinfo->cpptype) { if (c.first == tinfo->cpptype) {
auto *parentptr = c.second(valueptr); auto *parentptr = c.second(valueptr);
if (parentptr != valueptr) if (parentptr != valueptr) {
f(parentptr, self); f(parentptr, self);
}
traverse_offset_bases(parentptr, parent_tinfo, self, f); traverse_offset_bases(parentptr, parent_tinfo, self, f);
break; break;
} }
@ -311,31 +324,33 @@ inline bool deregister_instance_impl(void *ptr, instance *self) {
inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { inline void register_instance(instance *self, void *valptr, const type_info *tinfo) {
register_instance_impl(valptr, self); register_instance_impl(valptr, self);
if (!tinfo->simple_ancestors) if (!tinfo->simple_ancestors) {
traverse_offset_bases(valptr, tinfo, self, register_instance_impl); traverse_offset_bases(valptr, tinfo, self, register_instance_impl);
}
} }
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) {
bool ret = deregister_instance_impl(valptr, self); bool ret = deregister_instance_impl(valptr, self);
if (!tinfo->simple_ancestors) if (!tinfo->simple_ancestors) {
traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl);
}
return ret; return ret;
} }
/// Instance creation function for all pybind11 types. It allocates the internal instance layout for /// Instance creation function for all pybind11 types. It allocates the internal instance layout
/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast /// for holding C++ objects and holders. Allocation is done lazily (the first time the instance is
/// to a reference or pointer), and initialization is done by an `__init__` function. /// cast to a reference or pointer), and initialization is done by an `__init__` function.
inline PyObject *make_new_instance(PyTypeObject *type) { inline PyObject *make_new_instance(PyTypeObject *type) {
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first
// object is a plain Python type (i.e. not derived from an extension type). Fix it. // inherited object is a plain Python type (i.e. not derived from an extension type). Fix it.
ssize_t instance_size = static_cast<ssize_t>(sizeof(instance)); ssize_t instance_size = static_cast<ssize_t>(sizeof(instance));
if (type->tp_basicsize < instance_size) { if (type->tp_basicsize < instance_size) {
type->tp_basicsize = instance_size; type->tp_basicsize = instance_size;
} }
#endif #endif
PyObject *self = type->tp_alloc(type, 0); PyObject *self = type->tp_alloc(type, 0);
auto inst = reinterpret_cast<instance *>(self); auto *inst = reinterpret_cast<instance *>(self);
// Allocate the value/holder internals: // Allocate the value/holder internals:
inst->allocate_layout(); inst->allocate_layout();
@ -360,14 +375,14 @@ extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject
inline void add_patient(PyObject *nurse, PyObject *patient) { inline void add_patient(PyObject *nurse, PyObject *patient) {
auto &internals = get_internals(); auto &internals = get_internals();
auto instance = reinterpret_cast<detail::instance *>(nurse); auto *instance = reinterpret_cast<detail::instance *>(nurse);
instance->has_patients = true; instance->has_patients = true;
Py_INCREF(patient); Py_INCREF(patient);
internals.patients[nurse].push_back(patient); internals.patients[nurse].push_back(patient);
} }
inline void clear_patients(PyObject *self) { inline void clear_patients(PyObject *self) {
auto instance = reinterpret_cast<detail::instance *>(self); auto *instance = reinterpret_cast<detail::instance *>(self);
auto &internals = get_internals(); auto &internals = get_internals();
auto pos = internals.patients.find(self); auto pos = internals.patients.find(self);
assert(pos != internals.patients.end()); assert(pos != internals.patients.end());
@ -377,14 +392,15 @@ inline void clear_patients(PyObject *self) {
auto patients = std::move(pos->second); auto patients = std::move(pos->second);
internals.patients.erase(pos); internals.patients.erase(pos);
instance->has_patients = false; instance->has_patients = false;
for (PyObject *&patient : patients) for (PyObject *&patient : patients) {
Py_CLEAR(patient); Py_CLEAR(patient);
}
} }
/// Clears all internal data from the instance and removes it from registered instances in /// Clears all internal data from the instance and removes it from registered instances in
/// preparation for deallocation. /// preparation for deallocation.
inline void clear_instance(PyObject *self) { inline void clear_instance(PyObject *self) {
auto instance = reinterpret_cast<detail::instance *>(self); auto *instance = reinterpret_cast<detail::instance *>(self);
// Deallocate any values/holders, if present: // Deallocate any values/holders, if present:
for (auto &v_h : values_and_holders(instance)) { for (auto &v_h : values_and_holders(instance)) {
@ -392,25 +408,32 @@ inline void clear_instance(PyObject *self) {
// We have to deregister before we call dealloc because, for virtual MI types, we still // We have to deregister before we call dealloc because, for virtual MI types, we still
// need to be able to get the parent pointers. // need to be able to get the parent pointers.
if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) if (v_h.instance_registered()
pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) {
pybind11_fail(
"pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
}
if (instance->owned || v_h.holder_constructed()) if (instance->owned || v_h.holder_constructed()) {
v_h.type->dealloc(v_h); v_h.type->dealloc(v_h);
}
} }
} }
// Deallocate the value/holder layout internals: // Deallocate the value/holder layout internals:
instance->deallocate_layout(); instance->deallocate_layout();
if (instance->weakrefs) if (instance->weakrefs) {
PyObject_ClearWeakRefs(self); PyObject_ClearWeakRefs(self);
}
PyObject **dict_ptr = _PyObject_GetDictPtr(self); PyObject **dict_ptr = _PyObject_GetDictPtr(self);
if (dict_ptr) if (dict_ptr) {
Py_CLEAR(*dict_ptr); Py_CLEAR(*dict_ptr);
}
if (instance->has_patients) if (instance->has_patients) {
clear_patients(self); clear_patients(self);
}
} }
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` /// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
@ -418,7 +441,7 @@ inline void clear_instance(PyObject *self) {
extern "C" inline void pybind11_object_dealloc(PyObject *self) { extern "C" inline void pybind11_object_dealloc(PyObject *self) {
clear_instance(self); clear_instance(self);
auto type = Py_TYPE(self); auto *type = Py_TYPE(self);
type->tp_free(self); type->tp_free(self);
#if PY_VERSION_HEX < 0x03080000 #if PY_VERSION_HEX < 0x03080000
@ -447,16 +470,17 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
issue no Python C API calls which could potentially invoke the issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */ turn find the newly constructed type in an invalid state) */
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
if (!heap_type) if (!heap_type) {
pybind11_fail("make_object_base_type(): error allocating type!"); pybind11_fail("make_object_base_type(): error allocating type!");
}
heap_type->ht_name = name_obj.inc_ref().ptr(); heap_type->ht_name = name_obj.inc_ref().ptr();
#ifdef PYBIND11_BUILTIN_QUALNAME #ifdef PYBIND11_BUILTIN_QUALNAME
heap_type->ht_qualname = name_obj.inc_ref().ptr(); heap_type->ht_qualname = name_obj.inc_ref().ptr();
#endif #endif
auto type = &heap_type->ht_type; auto *type = &heap_type->ht_type;
type->tp_name = name; type->tp_name = name;
type->tp_base = type_incref(&PyBaseObject_Type); type->tp_base = type_incref(&PyBaseObject_Type);
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance)); type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
@ -469,8 +493,9 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
/* Support weak references (needed for the keep_alive feature) */ /* Support weak references (needed for the keep_alive feature) */
type->tp_weaklistoffset = offsetof(instance, weakrefs); type->tp_weaklistoffset = offsetof(instance, weakrefs);
if (PyType_Ready(type) < 0) if (PyType_Ready(type) < 0) {
pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
}
setattr((PyObject *) type, "__module__", str("pybind11_builtins")); setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
@ -482,8 +507,9 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
/// dynamic_attr: Support for `d = instance.__dict__`. /// dynamic_attr: Support for `d = instance.__dict__`.
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
PyObject *&dict = *_PyObject_GetDictPtr(self); PyObject *&dict = *_PyObject_GetDictPtr(self);
if (!dict) if (!dict) {
dict = PyDict_New(); dict = PyDict_New();
}
Py_XINCREF(dict); Py_XINCREF(dict);
return dict; return dict;
} }
@ -491,7 +517,8 @@ extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
/// dynamic_attr: Support for `instance.__dict__ = dict()`. /// dynamic_attr: Support for `instance.__dict__ = dict()`.
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
if (!PyDict_Check(new_dict)) { if (!PyDict_Check(new_dict)) {
PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", PyErr_Format(PyExc_TypeError,
"__dict__ must be set to a dictionary, not a '%.200s'",
get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str()); get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str());
return -1; return -1;
} }
@ -518,17 +545,16 @@ extern "C" inline int pybind11_clear(PyObject *self) {
/// Give instances of this type a `__dict__` and opt into garbage collection. /// Give instances of this type a `__dict__` and opt into garbage collection.
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
auto type = &heap_type->ht_type; auto *type = &heap_type->ht_type;
type->tp_flags |= Py_TPFLAGS_HAVE_GC; type->tp_flags |= Py_TPFLAGS_HAVE_GC;
type->tp_dictoffset = type->tp_basicsize; // place dict at the end type->tp_dictoffset = type->tp_basicsize; // place dict at the end
type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
type->tp_traverse = pybind11_traverse; type->tp_traverse = pybind11_traverse;
type->tp_clear = pybind11_clear; type->tp_clear = pybind11_clear;
static PyGetSetDef getset[] = { static PyGetSetDef getset[] = {
{const_cast<char*>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, {const_cast<char *>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} {nullptr, nullptr, nullptr, nullptr, nullptr}};
};
type->tp_getset = getset; type->tp_getset = getset;
} }
@ -538,12 +564,14 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
type_info *tinfo = nullptr; type_info *tinfo = nullptr;
for (auto type : reinterpret_borrow<tuple>(Py_TYPE(obj)->tp_mro)) { for (auto type : reinterpret_borrow<tuple>(Py_TYPE(obj)->tp_mro)) {
tinfo = get_type_info((PyTypeObject *) type.ptr()); tinfo = get_type_info((PyTypeObject *) type.ptr());
if (tinfo && tinfo->get_buffer) if (tinfo && tinfo->get_buffer) {
break; break;
}
} }
if (view == nullptr || !tinfo || !tinfo->get_buffer) { if (view == nullptr || !tinfo || !tinfo->get_buffer) {
if (view) if (view) {
view->obj = nullptr; view->obj = nullptr;
}
PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
return -1; return -1;
} }
@ -561,15 +589,17 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
view->buf = info->ptr; view->buf = info->ptr;
view->itemsize = info->itemsize; view->itemsize = info->itemsize;
view->len = view->itemsize; view->len = view->itemsize;
for (auto s : info->shape) for (auto s : info->shape) {
view->len *= s; view->len *= s;
}
view->readonly = static_cast<int>(info->readonly); view->readonly = static_cast<int>(info->readonly);
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) {
view->format = const_cast<char *>(info->format.c_str()); view->format = const_cast<char *>(info->format.c_str());
}
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
view->ndim = (int) info->ndim; view->ndim = (int) info->ndim;
view->strides = &info->strides[0]; view->strides = info->strides.data();
view->shape = &info->shape[0]; view->shape = info->shape.data();
} }
Py_INCREF(view->obj); Py_INCREF(view->obj);
return 0; return 0;
@ -593,7 +623,7 @@ inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
/** Create a brand new Python type according to the `type_record` specification. /** Create a brand new Python type according to the `type_record` specification.
Return value: New reference. */ Return value: New reference. */
inline PyObject* make_new_python_type(const type_record &rec) { inline PyObject *make_new_python_type(const type_record &rec) {
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name)); auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name));
auto qualname = name; auto qualname = name;
@ -608,55 +638,57 @@ inline PyObject* make_new_python_type(const type_record &rec) {
object module_; object module_;
if (rec.scope) { if (rec.scope) {
if (hasattr(rec.scope, "__module__")) if (hasattr(rec.scope, "__module__")) {
module_ = rec.scope.attr("__module__"); module_ = rec.scope.attr("__module__");
else if (hasattr(rec.scope, "__name__")) } else if (hasattr(rec.scope, "__name__")) {
module_ = rec.scope.attr("__name__"); module_ = rec.scope.attr("__name__");
}
} }
auto full_name = c_str( const auto *full_name = c_str(
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
module_ ? str(module_).cast<std::string>() + "." + rec.name : module_ ? str(module_).cast<std::string>() + "." + rec.name :
#endif #endif
rec.name); rec.name);
char *tp_doc = nullptr; char *tp_doc = nullptr;
if (rec.doc && options::show_user_defined_docstrings()) { if (rec.doc && options::show_user_defined_docstrings()) {
/* Allocate memory for docstring (using PyObject_MALLOC, since /* Allocate memory for docstring (using PyObject_MALLOC, since
Python will free this later on) */ Python will free this later on) */
size_t size = strlen(rec.doc) + 1; size_t size = std::strlen(rec.doc) + 1;
tp_doc = (char *) PyObject_MALLOC(size); tp_doc = (char *) PyObject_MALLOC(size);
memcpy((void *) tp_doc, rec.doc, size); std::memcpy((void *) tp_doc, rec.doc, size);
} }
auto &internals = get_internals(); auto &internals = get_internals();
auto bases = tuple(rec.bases); auto bases = tuple(rec.bases);
auto base = (bases.empty()) ? internals.instance_base auto *base = (bases.empty()) ? internals.instance_base : bases[0].ptr();
: bases[0].ptr();
/* Danger zone: from now (and until PyType_Ready), make sure to /* Danger zone: from now (and until PyType_Ready), make sure to
issue no Python C API calls which could potentially invoke the issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */ turn find the newly constructed type in an invalid state) */
auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() auto *metaclass
: internals.default_metaclass; = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() : internals.default_metaclass;
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
if (!heap_type) if (!heap_type) {
pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); pybind11_fail(std::string(rec.name) + ": Unable to create type object!");
}
heap_type->ht_name = name.release().ptr(); heap_type->ht_name = name.release().ptr();
#ifdef PYBIND11_BUILTIN_QUALNAME #ifdef PYBIND11_BUILTIN_QUALNAME
heap_type->ht_qualname = qualname.inc_ref().ptr(); heap_type->ht_qualname = qualname.inc_ref().ptr();
#endif #endif
auto type = &heap_type->ht_type; auto *type = &heap_type->ht_type;
type->tp_name = full_name; type->tp_name = full_name;
type->tp_doc = tp_doc; type->tp_doc = tp_doc;
type->tp_base = type_incref((PyTypeObject *)base); type->tp_base = type_incref((PyTypeObject *) base);
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance)); type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
if (!bases.empty()) if (!bases.empty()) {
type->tp_bases = bases.release().ptr(); type->tp_bases = bases.release().ptr();
}
/* Don't inherit base __init__ */ /* Don't inherit base __init__ */
type->tp_init = pybind11_object_init; type->tp_init = pybind11_object_init;
@ -674,29 +706,38 @@ inline PyObject* make_new_python_type(const type_record &rec) {
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
type->tp_flags |= Py_TPFLAGS_CHECKTYPES; type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
#endif #endif
if (!rec.is_final) if (!rec.is_final) {
type->tp_flags |= Py_TPFLAGS_BASETYPE; type->tp_flags |= Py_TPFLAGS_BASETYPE;
}
if (rec.dynamic_attr) if (rec.dynamic_attr) {
enable_dynamic_attributes(heap_type); enable_dynamic_attributes(heap_type);
}
if (rec.buffer_protocol) if (rec.buffer_protocol) {
enable_buffer_protocol(heap_type); enable_buffer_protocol(heap_type);
}
if (PyType_Ready(type) < 0) if (rec.custom_type_setup_callback) {
rec.custom_type_setup_callback(heap_type);
}
if (PyType_Ready(type) < 0) {
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");
}
assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
: !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
/* Register type with the parent scope */ /* Register type with the parent scope */
if (rec.scope) if (rec.scope) {
setattr(rec.scope, rec.name, (PyObject *) type); setattr(rec.scope, rec.name, (PyObject *) type);
else } else {
Py_INCREF(type); // Keep it alive forever (reference leak) Py_INCREF(type); // Keep it alive forever (reference leak)
}
if (module_) // Needed by pydoc if (module_) { // Needed by pydoc
setattr((PyObject *) type, "__module__", module_); setattr((PyObject *) type, "__module__", module_);
}
PYBIND11_SET_OLDPY_QUALNAME(type, qualname); PYBIND11_SET_OLDPY_QUALNAME(type, qualname);

File diff suppressed because it is too large Load Diff

View File

@ -15,9 +15,9 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if !defined(_MSC_VER) #if !defined(_MSC_VER)
# define PYBIND11_DESCR_CONSTEXPR static constexpr # define PYBIND11_DESCR_CONSTEXPR static constexpr
#else #else
# define PYBIND11_DESCR_CONSTEXPR const # define PYBIND11_DESCR_CONSTEXPR const
#endif #endif
/* Concatenate type signatures at compile time */ /* Concatenate type signatures at compile time */
@ -26,13 +26,15 @@ struct descr {
char text[N + 1]{'\0'}; char text[N + 1]{'\0'};
constexpr descr() = default; constexpr descr() = default;
constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { } // NOLINTNEXTLINE(google-explicit-constructor)
constexpr descr(char const (&s)[N + 1]) : descr(s, make_index_sequence<N>()) {}
template <size_t... Is> template <size_t... Is>
constexpr descr(char const (&s)[N+1], index_sequence<Is...>) : text{s[Is]..., '\0'} { } constexpr descr(char const (&s)[N + 1], index_sequence<Is...>) : text{s[Is]..., '\0'} {}
template <typename... Chars> template <typename... Chars>
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} { } // NOLINTNEXTLINE(google-explicit-constructor)
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} {}
static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() { static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() {
return {{&typeid(Ts)..., nullptr}}; return {{&typeid(Ts)..., nullptr}};
@ -40,60 +42,116 @@ struct descr {
}; };
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2> template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2>
constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b, constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a,
index_sequence<Is1...>, index_sequence<Is2...>) { const descr<N2, Ts2...> &b,
index_sequence<Is1...>,
index_sequence<Is2...>) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b);
return {a.text[Is1]..., b.text[Is2]...}; return {a.text[Is1]..., b.text[Is2]...};
} }
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2> template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
constexpr descr<N1 + N2, Ts1..., Ts2...> operator+(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b) { constexpr descr<N1 + N2, Ts1..., Ts2...> operator+(const descr<N1, Ts1...> &a,
const descr<N2, Ts2...> &b) {
return plus_impl(a, b, make_index_sequence<N1>(), make_index_sequence<N2>()); return plus_impl(a, b, make_index_sequence<N1>(), make_index_sequence<N2>());
} }
template <size_t N> template <size_t N>
constexpr descr<N - 1> _x(char const(&text)[N]) { return descr<N - 1>(text); } constexpr descr<N - 1> const_name(char const (&text)[N]) {
constexpr descr<0> _x(char const(&)[1]) { return {}; } return descr<N - 1>(text);
}
constexpr descr<0> const_name(char const (&)[1]) { return {}; }
template <size_t Rem, size_t... Digits> struct int_to_str : int_to_str<Rem/10, Rem%10, Digits...> { }; template <size_t Rem, size_t... Digits>
template <size_t...Digits> struct int_to_str<0, Digits...> { struct int_to_str : int_to_str<Rem / 10, Rem % 10, Digits...> {};
template <size_t... Digits>
struct int_to_str<0, Digits...> {
// WARNING: This only works with C++17 or higher.
static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...); static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...);
}; };
// Ternary description (like std::conditional) // Ternary description (like std::conditional)
template <bool B, size_t N1, size_t N2> template <bool B, size_t N1, size_t N2>
constexpr enable_if_t<B, descr<N1 - 1>> _x(char const(&text1)[N1], char const(&)[N2]) { constexpr enable_if_t<B, descr<N1 - 1>> const_name(char const (&text1)[N1], char const (&)[N2]) {
return _x(text1); return const_name(text1);
} }
template <bool B, size_t N1, size_t N2> template <bool B, size_t N1, size_t N2>
constexpr enable_if_t<!B, descr<N2 - 1>> _x(char const(&)[N1], char const(&text2)[N2]) { constexpr enable_if_t<!B, descr<N2 - 1>> const_name(char const (&)[N1], char const (&text2)[N2]) {
return _x(text2); return const_name(text2);
} }
template <bool B, typename T1, typename T2> template <bool B, typename T1, typename T2>
constexpr enable_if_t<B, T1> _x(const T1 &d, const T2 &) { return d; } constexpr enable_if_t<B, T1> const_name(const T1 &d, const T2 &) {
return d;
}
template <bool B, typename T1, typename T2> template <bool B, typename T1, typename T2>
constexpr enable_if_t<!B, T2> _x(const T1 &, const T2 &d) { return d; } constexpr enable_if_t<!B, T2> const_name(const T1 &, const T2 &d) {
return d;
}
template <size_t Size> auto constexpr _x() -> decltype(int_to_str<Size / 10, Size % 10>::digits) { template <size_t Size>
auto constexpr const_name() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
return int_to_str<Size / 10, Size % 10>::digits; return int_to_str<Size / 10, Size % 10>::digits;
} }
template <typename Type> constexpr descr<1, Type> _x() { return {'%'}; } template <typename Type>
constexpr descr<1, Type> const_name() {
return {'%'};
}
// If "_" is defined as a macro, py::detail::_ cannot be provided.
// It is therefore best to use py::detail::const_name universally.
// This block is for backward compatibility only.
// (The const_name code is repeated to avoid introducing a "_" #define ourselves.)
#ifndef _
# define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
template <size_t N>
constexpr descr<N - 1> _(char const (&text)[N]) {
return const_name<N>(text);
}
template <bool B, size_t N1, size_t N2>
constexpr enable_if_t<B, descr<N1 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
return const_name<B, N1, N2>(text1, text2);
}
template <bool B, size_t N1, size_t N2>
constexpr enable_if_t<!B, descr<N2 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
return const_name<B, N1, N2>(text1, text2);
}
template <bool B, typename T1, typename T2>
constexpr enable_if_t<B, T1> _(const T1 &d1, const T2 &d2) {
return const_name<B, T1, T2>(d1, d2);
}
template <bool B, typename T1, typename T2>
constexpr enable_if_t<!B, T2> _(const T1 &d1, const T2 &d2) {
return const_name<B, T1, T2>(d1, d2);
}
template <size_t Size>
auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
return const_name<Size>();
}
template <typename Type>
constexpr descr<1, Type> _() {
return const_name<Type>();
}
#endif // #ifndef _
constexpr descr<0> concat() { return {}; } constexpr descr<0> concat() { return {}; }
template <size_t N, typename... Ts> template <size_t N, typename... Ts>
constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) { return descr; } constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) {
return descr;
}
template <size_t N, typename... Ts, typename... Args> template <size_t N, typename... Ts, typename... Args>
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args) constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
-> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) { -> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
return d + _x(", ") + concat(args...); return d + const_name(", ") + concat(args...);
} }
template <size_t N, typename... Ts> template <size_t N, typename... Ts>
constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) { constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
return _x("{") + descr + _x("}"); return const_name("{") + descr + const_name("}");
} }
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

View File

@ -22,9 +22,10 @@ public:
return true; return true;
} }
template <typename> using cast_op_type = value_and_holder &; template <typename>
operator value_and_holder &() { return *value; } using cast_op_type = value_and_holder &;
static constexpr auto name = _x<value_and_holder>(); explicit operator value_and_holder &() { return *value; }
static constexpr auto name = const_name<value_and_holder>();
private: private:
value_and_holder *value = nullptr; value_and_holder *value = nullptr;
@ -33,15 +34,21 @@ private:
PYBIND11_NAMESPACE_BEGIN(initimpl) PYBIND11_NAMESPACE_BEGIN(initimpl)
inline void no_nullptr(void *ptr) { inline void no_nullptr(void *ptr) {
if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); if (!ptr) {
throw type_error("pybind11::init(): factory function returned nullptr");
}
} }
// Implementing functions for all forms of py::init<...> and py::init(...) // Implementing functions for all forms of py::init<...> and py::init(...)
template <typename Class> using Cpp = typename Class::type; template <typename Class>
template <typename Class> using Alias = typename Class::type_alias; using Cpp = typename Class::type;
template <typename Class> using Holder = typename Class::holder_type; template <typename Class>
using Alias = typename Class::type_alias;
template <typename Class>
using Holder = typename Class::holder_type;
template <typename Class> using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>; template <typename Class>
using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>;
// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. // Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance.
template <typename Class, enable_if_t<Class::has_alias, int> = 0> template <typename Class, enable_if_t<Class::has_alias, int> = 0>
@ -50,17 +57,27 @@ bool is_alias(Cpp<Class> *ptr) {
} }
// Failing fallback version of the above for a no-alias class (always returns false) // Failing fallback version of the above for a no-alias class (always returns false)
template <typename /*Class*/> template <typename /*Class*/>
constexpr bool is_alias(void *) { return false; } constexpr bool is_alias(void *) {
return false;
}
// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall // Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
// back to brace aggregate initiailization so that for aggregate initialization can be used with // back to brace aggregate initiailization so that for aggregate initialization can be used with
// py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For // py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For
// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually // non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
// works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor). // works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).
template <typename Class, typename... Args, detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0> template <typename Class,
inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward<Args>(args)...); } typename... Args,
template <typename Class, typename... Args, detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0> detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0>
inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward<Args>(args)...}; } inline Class *construct_or_initialize(Args &&...args) {
return new Class(std::forward<Args>(args)...);
}
template <typename Class,
typename... Args,
detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0>
inline Class *construct_or_initialize(Args &&...args) {
return new Class{std::forward<Args>(args)...};
}
// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with // Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with
// an alias to provide only a single Cpp factory function as long as the Alias can be // an alias to provide only a single Cpp factory function as long as the Alias can be
@ -69,12 +86,14 @@ inline Class *construct_or_initialize(Args &&...args) { return new Class{std::fo
// inherit all the base class constructors. // inherit all the base class constructors.
template <typename Class> template <typename Class>
void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/,
value_and_holder &v_h, Cpp<Class> &&base) { value_and_holder &v_h,
Cpp<Class> &&base) {
v_h.value_ptr() = new Alias<Class>(std::move(base)); v_h.value_ptr() = new Alias<Class>(std::move(base));
} }
template <typename Class> template <typename Class>
[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, [[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/,
value_and_holder &, Cpp<Class> &&) { value_and_holder &,
Cpp<Class> &&) {
throw type_error("pybind11::init(): unable to convert returned instance to required " throw type_error("pybind11::init(): unable to convert returned instance to required "
"alias class: no `Alias<Class>(Class &&)` constructor available"); "alias class: no `Alias<Class>(Class &&)` constructor available");
} }
@ -84,8 +103,8 @@ template <typename Class>
template <typename Class> template <typename Class>
void construct(...) { void construct(...) {
static_assert(!std::is_same<Class, Class>::value /* always false */, static_assert(!std::is_same<Class, Class>::value /* always false */,
"pybind11::init(): init function must return a compatible pointer, " "pybind11::init(): init function must return a compatible pointer, "
"holder, or value"); "holder, or value");
} }
// Pointer return v1: the factory function returns a class pointer for a registered class. // Pointer return v1: the factory function returns a class pointer for a registered class.
@ -94,8 +113,9 @@ void construct(...) {
// construct an Alias from the returned base instance. // construct an Alias from the returned base instance.
template <typename Class> template <typename Class>
void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) { void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
no_nullptr(ptr); no_nullptr(ptr);
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) { if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) {
// We're going to try to construct an alias by moving the cpp type. Whether or not // We're going to try to construct an alias by moving the cpp type. Whether or not
// that succeeds, we still need to destroy the original cpp pointer (either the // that succeeds, we still need to destroy the original cpp pointer (either the
// moved away leftover, if the alias construction works, or the value itself if we // moved away leftover, if the alias construction works, or the value itself if we
@ -105,7 +125,7 @@ void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
// the holder and destruction happens when we leave the C++ scope, and the holder // the holder and destruction happens when we leave the C++ scope, and the holder
// class gets to handle the destruction however it likes. // class gets to handle the destruction however it likes.
v_h.value_ptr() = ptr; v_h.value_ptr() = ptr;
v_h.set_instance_registered(true); // To prevent init_instance from registering it v_h.set_instance_registered(true); // To prevent init_instance from registering it
v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder
Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder
v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null
@ -128,15 +148,18 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
// Holder return: copy its pointer, and move or copy the returned holder into the new instance's // Holder return: copy its pointer, and move or copy the returned holder into the new instance's
// holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a // holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a
// derived type (through those holder's implicit conversion from derived class holder constructors). // derived type (through those holder's implicit conversion from derived class holder
// constructors).
template <typename Class> template <typename Class>
void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
auto *ptr = holder_helper<Holder<Class>>::get(holder); auto *ptr = holder_helper<Holder<Class>>::get(holder);
no_nullptr(ptr); no_nullptr(ptr);
// If we need an alias, check that the held pointer is actually an alias instance // If we need an alias, check that the held pointer is actually an alias instance
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) {
throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance "
"is not an alias instance"); "is not an alias instance");
}
v_h.value_ptr() = ptr; v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &holder); v_h.type->init_instance(v_h.inst, &holder);
@ -148,12 +171,14 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
// need it, we simply move-construct the cpp value into a new instance. // need it, we simply move-construct the cpp value into a new instance.
template <typename Class> template <typename Class>
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) { void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
static_assert(std::is_move_constructible<Cpp<Class>>::value, static_assert(std::is_move_constructible<Cpp<Class>>::value,
"pybind11::init() return-by-value factory function requires a movable class"); "pybind11::init() return-by-value factory function requires a movable class");
if (Class::has_alias && need_alias) if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias) {
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result)); construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
else } else {
v_h.value_ptr() = new Cpp<Class>(std::move(result)); v_h.value_ptr() = new Cpp<Class>(std::move(result));
}
} }
// return-by-value version 2: returning a value of the alias type itself. We move-construct an // return-by-value version 2: returning a value of the alias type itself. We move-construct an
@ -161,7 +186,8 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
// cases where Alias initialization is always desired. // cases where Alias initialization is always desired.
template <typename Class> template <typename Class>
void construct(value_and_holder &v_h, Alias<Class> &&result, bool) { void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
static_assert(std::is_move_constructible<Alias<Class>>::value, static_assert(
std::is_move_constructible<Alias<Class>>::value,
"pybind11::init() return-by-alias-value factory function requires a movable alias class"); "pybind11::init() return-by-alias-value factory function requires a movable alias class");
v_h.value_ptr() = new Alias<Class>(std::move(result)); v_h.value_ptr() = new Alias<Class>(std::move(result));
} }
@ -170,48 +196,76 @@ void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
template <typename... Args> template <typename... Args>
struct constructor { struct constructor {
template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0> template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0>
static void execute(Class &cl, const Extra&... extra) { static void execute(Class &cl, const Extra &...extra) {
cl.def("__init__", [](value_and_holder &v_h, Args... args) { cl.def(
v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); "__init__",
}, is_new_style_constructor(), extra...); [](value_and_holder &v_h, Args... args) {
}
template <typename Class, typename... Extra,
enable_if_t<Class::has_alias &&
std::is_constructible<Cpp<Class>, Args...>::value, int> = 0>
static void execute(Class &cl, const Extra&... extra) {
cl.def("__init__", [](value_and_holder &v_h, Args... args) {
if (Py_TYPE(v_h.inst) == v_h.type->type)
v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
else },
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); is_new_style_constructor(),
}, is_new_style_constructor(), extra...); extra...);
} }
template <typename Class, typename... Extra, template <typename Class,
enable_if_t<Class::has_alias && typename... Extra,
!std::is_constructible<Cpp<Class>, Args...>::value, int> = 0> enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value,
static void execute(Class &cl, const Extra&... extra) { int> = 0>
cl.def("__init__", [](value_and_holder &v_h, Args... args) { static void execute(Class &cl, const Extra &...extra) {
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); cl.def(
}, is_new_style_constructor(), extra...); "__init__",
[](value_and_holder &v_h, Args... args) {
if (Py_TYPE(v_h.inst) == v_h.type->type) {
v_h.value_ptr()
= construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
} else {
v_h.value_ptr()
= construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
}
},
is_new_style_constructor(),
extra...);
}
template <typename Class,
typename... Extra,
enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value,
int> = 0>
static void execute(Class &cl, const Extra &...extra) {
cl.def(
"__init__",
[](value_and_holder &v_h, Args... args) {
v_h.value_ptr()
= construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
},
is_new_style_constructor(),
extra...);
} }
}; };
// Implementing class for py::init_alias<...>() // Implementing class for py::init_alias<...>()
template <typename... Args> struct alias_constructor { template <typename... Args>
template <typename Class, typename... Extra, struct alias_constructor {
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int> = 0> template <typename Class,
static void execute(Class &cl, const Extra&... extra) { typename... Extra,
cl.def("__init__", [](value_and_holder &v_h, Args... args) { enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value,
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); int> = 0>
}, is_new_style_constructor(), extra...); static void execute(Class &cl, const Extra &...extra) {
cl.def(
"__init__",
[](value_and_holder &v_h, Args... args) {
v_h.value_ptr()
= construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
},
is_new_style_constructor(),
extra...);
} }
}; };
// Implementation class for py::init(Func) and py::init(Func, AliasFunc) // Implementation class for py::init(Func) and py::init(Func, AliasFunc)
template <typename CFunc, typename AFunc = void_type (*)(), template <typename CFunc,
typename = function_signature_t<CFunc>, typename = function_signature_t<AFunc>> typename AFunc = void_type (*)(),
typename = function_signature_t<CFunc>,
typename = function_signature_t<AFunc>>
struct factory; struct factory;
// Specialization for py::init(Func) // Specialization for py::init(Func)
@ -219,7 +273,8 @@ template <typename Func, typename Return, typename... Args>
struct factory<Func, void_type (*)(), Return(Args...)> { struct factory<Func, void_type (*)(), Return(Args...)> {
remove_reference_t<Func> class_factory; remove_reference_t<Func> class_factory;
factory(Func &&f) : class_factory(std::forward<Func>(f)) { } // NOLINTNEXTLINE(google-explicit-constructor)
factory(Func &&f) : class_factory(std::forward<Func>(f)) {}
// The given class either has no alias or has no separate alias factory; // The given class either has no alias or has no separate alias factory;
// this always constructs the class itself. If the class is registered with an alias // this always constructs the class itself. If the class is registered with an alias
@ -228,22 +283,32 @@ struct factory<Func, void_type (*)(), Return(Args...)> {
// instance, or the alias needs to be constructible from a `Class &&` argument. // instance, or the alias needs to be constructible from a `Class &&` argument.
template <typename Class, typename... Extra> template <typename Class, typename... Extra>
void execute(Class &cl, const Extra &...extra) && { void execute(Class &cl, const Extra &...extra) && {
#if defined(PYBIND11_CPP14) #if defined(PYBIND11_CPP14)
cl.def("__init__", [func = std::move(class_factory)] cl.def(
#else "__init__",
[func = std::move(class_factory)]
#else
auto &func = class_factory; auto &func = class_factory;
cl.def("__init__", [func] cl.def(
#endif "__init__",
(value_and_holder &v_h, Args... args) { [func]
construct<Class>(v_h, func(std::forward<Args>(args)...), #endif
Py_TYPE(v_h.inst) != v_h.type->type); (value_and_holder &v_h, Args... args) {
}, is_new_style_constructor(), extra...); construct<Class>(
v_h, func(std::forward<Args>(args)...), Py_TYPE(v_h.inst) != v_h.type->type);
},
is_new_style_constructor(),
extra...);
} }
}; };
// Specialization for py::init(Func, AliasFunc) // Specialization for py::init(Func, AliasFunc)
template <typename CFunc, typename AFunc, template <typename CFunc,
typename CReturn, typename... CArgs, typename AReturn, typename... AArgs> typename AFunc,
typename CReturn,
typename... CArgs,
typename AReturn,
typename... AArgs>
struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> { struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
static_assert(sizeof...(CArgs) == sizeof...(AArgs), static_assert(sizeof...(CArgs) == sizeof...(AArgs),
"pybind11::init(class_factory, alias_factory): class and alias factories " "pybind11::init(class_factory, alias_factory): class and alias factories "
@ -256,29 +321,37 @@ struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
remove_reference_t<AFunc> alias_factory; remove_reference_t<AFunc> alias_factory;
factory(CFunc &&c, AFunc &&a) factory(CFunc &&c, AFunc &&a)
: class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) { } : class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) {}
// The class factory is called when the `self` type passed to `__init__` is the direct // The class factory is called when the `self` type passed to `__init__` is the direct
// class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype.
template <typename Class, typename... Extra> template <typename Class, typename... Extra>
void execute(Class &cl, const Extra&... extra) && { void execute(Class &cl, const Extra &...extra) && {
static_assert(Class::has_alias, "The two-argument version of `py::init()` can " static_assert(Class::has_alias,
"only be used if the class has an alias"); "The two-argument version of `py::init()` can "
#if defined(PYBIND11_CPP14) "only be used if the class has an alias");
cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] #if defined(PYBIND11_CPP14)
#else cl.def(
"__init__",
[class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
#else
auto &class_func = class_factory; auto &class_func = class_factory;
auto &alias_func = alias_factory; auto &alias_func = alias_factory;
cl.def("__init__", [class_func, alias_func] cl.def(
#endif "__init__",
(value_and_holder &v_h, CArgs... args) { [class_func, alias_func]
if (Py_TYPE(v_h.inst) == v_h.type->type) #endif
// If the instance type equals the registered type we don't have inheritance, so (value_and_holder &v_h, CArgs... args) {
// don't need the alias and can construct using the class function: if (Py_TYPE(v_h.inst) == v_h.type->type) {
construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false); // If the instance type equals the registered type we don't have inheritance,
else // so don't need the alias and can construct using the class function:
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true); construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
}, is_new_style_constructor(), extra...); } else {
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
}
},
is_new_style_constructor(),
extra...);
} }
}; };
@ -289,7 +362,9 @@ void setstate(value_and_holder &v_h, T &&result, bool need_alias) {
} }
/// Set both the C++ and Python states /// Set both the C++ and Python states
template <typename Class, typename T, typename O, template <typename Class,
typename T,
typename O,
enable_if_t<std::is_convertible<O, handle>::value, int> = 0> enable_if_t<std::is_convertible<O, handle>::value, int> = 0>
void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) { void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) {
construct<Class>(v_h, std::move(result.first), need_alias); construct<Class>(v_h, std::move(result.first), need_alias);
@ -303,12 +378,18 @@ void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias)
} }
/// Implementation for py::pickle(GetState, SetState) /// Implementation for py::pickle(GetState, SetState)
template <typename Get, typename Set, template <typename Get,
typename = function_signature_t<Get>, typename = function_signature_t<Set>> typename Set,
typename = function_signature_t<Get>,
typename = function_signature_t<Set>>
struct pickle_factory; struct pickle_factory;
template <typename Get, typename Set, template <typename Get,
typename RetState, typename Self, typename NewInstance, typename ArgState> typename Set,
typename RetState,
typename Self,
typename NewInstance,
typename ArgState>
struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> { struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value, static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value,
"The type returned by `__getstate__` must be the same " "The type returned by `__getstate__` must be the same "
@ -317,23 +398,28 @@ struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
remove_reference_t<Get> get; remove_reference_t<Get> get;
remove_reference_t<Set> set; remove_reference_t<Set> set;
pickle_factory(Get get, Set set) pickle_factory(Get get, Set set) : get(std::forward<Get>(get)), set(std::forward<Set>(set)) {}
: get(std::forward<Get>(get)), set(std::forward<Set>(set)) { }
template <typename Class, typename... Extra> template <typename Class, typename... Extra>
void execute(Class &cl, const Extra &...extra) && { void execute(Class &cl, const Extra &...extra) && {
cl.def("__getstate__", std::move(get)); cl.def("__getstate__", std::move(get));
#if defined(PYBIND11_CPP14) #if defined(PYBIND11_CPP14)
cl.def("__setstate__", [func = std::move(set)] cl.def(
"__setstate__",
[func = std::move(set)]
#else #else
auto &func = set; auto &func = set;
cl.def("__setstate__", [func] cl.def(
"__setstate__",
[func]
#endif #endif
(value_and_holder &v_h, ArgState state) { (value_and_holder &v_h, ArgState state) {
setstate<Class>(v_h, func(std::forward<ArgState>(state)), setstate<Class>(
Py_TYPE(v_h.inst) != v_h.type->type); v_h, func(std::forward<ArgState>(state)), Py_TYPE(v_h.inst) != v_h.type->type);
}, is_new_style_constructor(), extra...); },
is_new_style_constructor(),
extra...);
} }
}; };

View File

@ -11,8 +11,32 @@
#include "../pytypes.h" #include "../pytypes.h"
#include <exception>
/// Tracks the `internals` and `type_info` ABI version independent of the main library version.
///
/// Some portions of the code use an ABI that is conditional depending on this
/// version number. That allows ABI-breaking changes to be "pre-implemented".
/// Once the default version number is incremented, the conditional logic that
/// no longer applies can be removed. Additionally, users that need not
/// maintain ABI compatibility can increase the version number in order to take
/// advantage of any functionality/efficiency improvements that depend on the
/// newer ABI.
///
/// WARNING: If you choose to manually increase the ABI version, note that
/// pybind11 may not be tested as thoroughly with a non-default ABI version, and
/// further ABI-incompatible changes may be made before the ABI is officially
/// changed to the new version.
#ifndef PYBIND11_INTERNALS_VERSION
# define PYBIND11_INTERNALS_VERSION 4
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
using ExceptionTranslator = void (*)(std::exception_ptr);
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
// Forward declarations // Forward declarations
inline PyTypeObject *make_static_property_type(); inline PyTypeObject *make_static_property_type();
inline PyTypeObject *make_default_metaclass(); inline PyTypeObject *make_default_metaclass();
@ -21,30 +45,59 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new // The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new
// Thread Specific Storage (TSS) API. // Thread Specific Storage (TSS) API.
#if PY_VERSION_HEX >= 0x03070000 #if PY_VERSION_HEX >= 0x03070000
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr // Avoid unnecessary allocation of `Py_tss_t`, since we cannot use
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) // `Py_LIMITED_API` anyway.
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value)) # if PYBIND11_INTERNALS_VERSION > 4
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) # define PYBIND11_TLS_KEY_REF Py_tss_t &
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key) # ifdef __GNUC__
#else // Clang on macOS warns due to `Py_tss_NEEDS_INIT` not specifying an initializer
// Usually an int but a long on Cygwin64 with Python 3.x // for every field.
# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0 # define PYBIND11_TLS_KEY_INIT(var) \
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) _Pragma("GCC diagnostic push") /**/ \
# if PY_MAJOR_VERSION < 3 _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
# define PYBIND11_TLS_DELETE_VALUE(key) \ Py_tss_t var \
PyThread_delete_key_value(key) = Py_tss_NEEDS_INIT; \
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ _Pragma("GCC diagnostic pop")
do { \ # else
PyThread_delete_key_value((key)); \ # define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
PyThread_set_key_value((key), (value)); \ # endif
} while (false) # define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0)
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr)
# define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key))
# else # else
# define PYBIND11_TLS_DELETE_VALUE(key) \ # define PYBIND11_TLS_KEY_REF Py_tss_t *
PyThread_set_key_value((key), nullptr) # define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr;
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ # define PYBIND11_TLS_KEY_CREATE(var) \
PyThread_set_key_value((key), (value)) (((var) = PyThread_tss_alloc()) != nullptr && (PyThread_tss_create((var)) == 0))
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
# endif # endif
# define PYBIND11_TLS_FREE(key) (void)key #else
// Usually an int but a long on Cygwin64 with Python 3.x
# define PYBIND11_TLS_KEY_REF decltype(PyThread_create_key())
# define PYBIND11_TLS_KEY_INIT(var) PYBIND11_TLS_KEY_REF var = 0;
# define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1)
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key))
# if PY_MAJOR_VERSION < 3 || defined(PYPY_VERSION)
// On CPython < 3.4 and on PyPy, `PyThread_set_key_value` strangely does not set
// the value if it has already been set. Instead, it must first be deleted and
// then set again.
inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) {
PyThread_delete_key_value(key);
PyThread_set_key_value(key, value);
}
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_delete_key_value(key)
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
::pybind11::detail::tls_replace_value((key), (value))
# else
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_set_key_value((key), nullptr)
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_set_key_value((key), (value))
# endif
# define PYBIND11_TLS_FREE(key) (void) key
#endif #endif
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly // Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
@ -66,8 +119,9 @@ struct type_hash {
size_t operator()(const std::type_index &t) const { size_t operator()(const std::type_index &t) const {
size_t hash = 5381; size_t hash = 5381;
const char *ptr = t.name(); const char *ptr = t.name();
while (auto c = static_cast<unsigned char>(*ptr++)) while (auto c = static_cast<unsigned char>(*ptr++)) {
hash = (hash * 33) ^ c; hash = (hash * 33) ^ c;
}
return hash; return hash;
} }
}; };
@ -83,9 +137,9 @@ template <typename value_type>
using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>; using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>;
struct override_hash { struct override_hash {
inline size_t operator()(const std::pair<const PyObject *, const char *>& v) const { inline size_t operator()(const std::pair<const PyObject *, const char *> &v) const {
size_t value = std::hash<const void *>()(v.first); size_t value = std::hash<const void *>()(v.first);
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value << 6) + (value >> 2);
return value; return value;
} }
}; };
@ -94,30 +148,44 @@ struct override_hash {
/// Whenever binary incompatible changes are made to this structure, /// Whenever binary incompatible changes are made to this structure,
/// `PYBIND11_INTERNALS_VERSION` must be incremented. /// `PYBIND11_INTERNALS_VERSION` must be incremented.
struct internals { struct internals {
type_map<type_info *> registered_types_cpp; // std::type_index -> pybind11's type information // std::type_index -> pybind11's type information
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; // PyTypeObject* -> base type_info(s) type_map<type_info *> registered_types_cpp;
std::unordered_multimap<const void *, instance*> registered_instances; // void * -> instance* // PyTypeObject* -> base type_info(s)
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash> inactive_override_cache; std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py;
std::unordered_multimap<const void *, instance *> registered_instances; // void * -> instance*
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash>
inactive_override_cache;
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions; type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
std::unordered_map<const PyObject *, std::vector<PyObject *>> patients; std::unordered_map<const PyObject *, std::vector<PyObject *>> patients;
std::forward_list<void (*) (std::exception_ptr)> registered_exception_translators; std::forward_list<ExceptionTranslator> registered_exception_translators;
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across
std::vector<PyObject *> loader_patient_stack; // Used by `loader_life_support` // extensions
std::forward_list<std::string> static_strings; // Stores the std::strings backing detail::c_str() #if PYBIND11_INTERNALS_VERSION == 4
std::vector<PyObject *> unused_loader_patient_stack_remove_at_v5;
#endif
std::forward_list<std::string> static_strings; // Stores the std::strings backing
// detail::c_str()
PyTypeObject *static_property_type; PyTypeObject *static_property_type;
PyTypeObject *default_metaclass; PyTypeObject *default_metaclass;
PyObject *instance_base; PyObject *instance_base;
#if defined(WITH_THREAD) #if defined(WITH_THREAD)
PYBIND11_TLS_KEY_INIT(tstate); PYBIND11_TLS_KEY_INIT(tstate)
# if PYBIND11_INTERNALS_VERSION > 4
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
# endif // PYBIND11_INTERNALS_VERSION > 4
PyInterpreterState *istate = nullptr; PyInterpreterState *istate = nullptr;
~internals() { ~internals() {
# if PYBIND11_INTERNALS_VERSION > 4
PYBIND11_TLS_FREE(loader_life_support_tls_key);
# endif // PYBIND11_INTERNALS_VERSION > 4
// This destructor is called *after* Py_Finalize() in finalize_interpreter(). // This destructor is called *after* Py_Finalize() in finalize_interpreter().
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is called. // That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is
// PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing. // called. PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does
// PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree. // nothing. PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
// PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither // PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX).
// of those have anything to do with CPython internals. // Neither of those have anything to do with CPython internals. PyMem_RawFree *requires*
// PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator. // that the `tstate` be allocated with the CPython allocator.
PYBIND11_TLS_FREE(tstate); PYBIND11_TLS_FREE(tstate);
} }
#endif #endif
@ -132,14 +200,16 @@ struct type_info {
void *(*operator_new)(size_t); void *(*operator_new)(size_t);
void (*init_instance)(instance *, const void *); void (*init_instance)(instance *, const void *);
void (*dealloc)(value_and_holder &v_h); void (*dealloc)(value_and_holder &v_h);
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions; std::vector<PyObject *(*) (PyObject *, PyTypeObject *)> implicit_conversions;
std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts; std::vector<std::pair<const std::type_info *, void *(*) (void *)>> implicit_casts;
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions; std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;
buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; buffer_info *(*get_buffer)(PyObject *, void *) = nullptr;
void *get_buffer_data = nullptr; void *get_buffer_data = nullptr;
void *(*module_local_load)(PyObject *, const type_info *) = nullptr; void *(*module_local_load)(PyObject *, const type_info *) = nullptr;
/* A simple type never occurs as a (direct or indirect) parent /* A simple type never occurs as a (direct or indirect) parent
* of a class that makes use of multiple inheritance */ * of a class that makes use of multiple inheritance.
* A type can be simple even if it has non-simple ancestors as long as it has no descendants.
*/
bool simple_type : 1; bool simple_type : 1;
/* True if there is no multiple inheritance in this type's inheritance tree */ /* True if there is no multiple inheritance in this type's inheritance tree */
bool simple_ancestors : 1; bool simple_ancestors : 1;
@ -149,72 +219,73 @@ struct type_info {
bool module_local : 1; bool module_local : 1;
}; };
/// Tracks the `internals` and `type_info` ABI version independent of the main library version
#define PYBIND11_INTERNALS_VERSION 4
/// On MSVC, debug and release builds are not ABI-compatible! /// On MSVC, debug and release builds are not ABI-compatible!
#if defined(_MSC_VER) && defined(_DEBUG) #if defined(_MSC_VER) && defined(_DEBUG)
# define PYBIND11_BUILD_TYPE "_debug" # define PYBIND11_BUILD_TYPE "_debug"
#else #else
# define PYBIND11_BUILD_TYPE "" # define PYBIND11_BUILD_TYPE ""
#endif #endif
/// Let's assume that different compilers are ABI-incompatible. /// Let's assume that different compilers are ABI-incompatible.
/// A user can manually set this string if they know their /// A user can manually set this string if they know their
/// compiler is compatible. /// compiler is compatible.
#ifndef PYBIND11_COMPILER_TYPE #ifndef PYBIND11_COMPILER_TYPE
# if defined(_MSC_VER) # if defined(_MSC_VER)
# define PYBIND11_COMPILER_TYPE "_msvc" # define PYBIND11_COMPILER_TYPE "_msvc"
# elif defined(__INTEL_COMPILER) # elif defined(__INTEL_COMPILER)
# define PYBIND11_COMPILER_TYPE "_icc" # define PYBIND11_COMPILER_TYPE "_icc"
# elif defined(__clang__) # elif defined(__clang__)
# define PYBIND11_COMPILER_TYPE "_clang" # define PYBIND11_COMPILER_TYPE "_clang"
# elif defined(__PGI) # elif defined(__PGI)
# define PYBIND11_COMPILER_TYPE "_pgi" # define PYBIND11_COMPILER_TYPE "_pgi"
# elif defined(__MINGW32__) # elif defined(__MINGW32__)
# define PYBIND11_COMPILER_TYPE "_mingw" # define PYBIND11_COMPILER_TYPE "_mingw"
# elif defined(__CYGWIN__) # elif defined(__CYGWIN__)
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" # define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
# elif defined(__GNUC__) # elif defined(__GNUC__)
# define PYBIND11_COMPILER_TYPE "_gcc" # define PYBIND11_COMPILER_TYPE "_gcc"
# else # else
# define PYBIND11_COMPILER_TYPE "_unknown" # define PYBIND11_COMPILER_TYPE "_unknown"
# endif # endif
#endif #endif
/// Also standard libs /// Also standard libs
#ifndef PYBIND11_STDLIB #ifndef PYBIND11_STDLIB
# if defined(_LIBCPP_VERSION) # if defined(_LIBCPP_VERSION)
# define PYBIND11_STDLIB "_libcpp" # define PYBIND11_STDLIB "_libcpp"
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) # elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
# define PYBIND11_STDLIB "_libstdcpp" # define PYBIND11_STDLIB "_libstdcpp"
# else # else
# define PYBIND11_STDLIB "" # define PYBIND11_STDLIB ""
# endif # endif
#endif #endif
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. /// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
#ifndef PYBIND11_BUILD_ABI #ifndef PYBIND11_BUILD_ABI
# if defined(__GXX_ABI_VERSION) # if defined(__GXX_ABI_VERSION)
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) # define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
# else # else
# define PYBIND11_BUILD_ABI "" # define PYBIND11_BUILD_ABI ""
# endif # endif
#endif #endif
#ifndef PYBIND11_INTERNALS_KIND #ifndef PYBIND11_INTERNALS_KIND
# if defined(WITH_THREAD) # if defined(WITH_THREAD)
# define PYBIND11_INTERNALS_KIND "" # define PYBIND11_INTERNALS_KIND ""
# else # else
# define PYBIND11_INTERNALS_KIND "_without_thread" # define PYBIND11_INTERNALS_KIND "_without_thread"
# endif # endif
#endif #endif
#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ #define PYBIND11_INTERNALS_ID \
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
PYBIND11_BUILD_TYPE "__"
#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ #define PYBIND11_MODULE_LOCAL_ID \
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" "__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
PYBIND11_BUILD_TYPE "__"
/// Each module locally stores a pointer to the `internals` data. The data /// Each module locally stores a pointer to the `internals` data. The data
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
@ -223,21 +294,104 @@ inline internals **&get_internals_pp() {
return internals_pp; return internals_pp;
} }
#if PY_VERSION_HEX >= 0x03030000
// forward decl
inline void translate_exception(std::exception_ptr);
template <class T,
enable_if_t<std::is_same<std::nested_exception, remove_cvref_t<T>>::value, int> = 0>
bool handle_nested_exception(const T &exc, const std::exception_ptr &p) {
std::exception_ptr nested = exc.nested_ptr();
if (nested != nullptr && nested != p) {
translate_exception(nested);
return true;
}
return false;
}
template <class T,
enable_if_t<!std::is_same<std::nested_exception, remove_cvref_t<T>>::value, int> = 0>
bool handle_nested_exception(const T &exc, const std::exception_ptr &p) {
if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(exc))) {
return handle_nested_exception(*nep, p);
}
return false;
}
#else
template <class T>
bool handle_nested_exception(const T &, std::exception_ptr &) {
return false;
}
#endif
inline bool raise_err(PyObject *exc_type, const char *msg) {
#if PY_VERSION_HEX >= 0x03030000
if (PyErr_Occurred()) {
raise_from(exc_type, msg);
return true;
}
#endif
PyErr_SetString(exc_type, msg);
return false;
}
inline void translate_exception(std::exception_ptr p) { inline void translate_exception(std::exception_ptr p) {
if (!p) {
return;
}
try { try {
if (p) std::rethrow_exception(p); std::rethrow_exception(p);
} catch (error_already_set &e) { e.restore(); return; } catch (error_already_set &e) {
} catch (const builtin_exception &e) { e.set_error(); return; handle_nested_exception(e, p);
} catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; e.restore();
} catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; return;
} catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; } catch (const builtin_exception &e) {
} catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; // Could not use template since it's an abstract class.
} catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(e))) {
} catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; handle_nested_exception(*nep, p);
} catch (const std::overflow_error &e) { PyErr_SetString(PyExc_OverflowError, e.what()); return; }
} catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; e.set_error();
return;
} catch (const std::bad_alloc &e) {
handle_nested_exception(e, p);
raise_err(PyExc_MemoryError, e.what());
return;
} catch (const std::domain_error &e) {
handle_nested_exception(e, p);
raise_err(PyExc_ValueError, e.what());
return;
} catch (const std::invalid_argument &e) {
handle_nested_exception(e, p);
raise_err(PyExc_ValueError, e.what());
return;
} catch (const std::length_error &e) {
handle_nested_exception(e, p);
raise_err(PyExc_ValueError, e.what());
return;
} catch (const std::out_of_range &e) {
handle_nested_exception(e, p);
raise_err(PyExc_IndexError, e.what());
return;
} catch (const std::range_error &e) {
handle_nested_exception(e, p);
raise_err(PyExc_ValueError, e.what());
return;
} catch (const std::overflow_error &e) {
handle_nested_exception(e, p);
raise_err(PyExc_OverflowError, e.what());
return;
} catch (const std::exception &e) {
handle_nested_exception(e, p);
raise_err(PyExc_RuntimeError, e.what());
return;
} catch (const std::nested_exception &e) {
handle_nested_exception(e, p);
raise_err(PyExc_RuntimeError, "Caught an unknown nested exception!");
return;
} catch (...) { } catch (...) {
PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); raise_err(PyExc_RuntimeError, "Caught an unknown exception!");
return; return;
} }
} }
@ -245,23 +399,30 @@ inline void translate_exception(std::exception_ptr p) {
#if !defined(__GLIBCXX__) #if !defined(__GLIBCXX__)
inline void translate_local_exception(std::exception_ptr p) { inline void translate_local_exception(std::exception_ptr p) {
try { try {
if (p) std::rethrow_exception(p); if (p) {
} catch (error_already_set &e) { e.restore(); return; std::rethrow_exception(p);
} catch (const builtin_exception &e) { e.set_error(); return; }
} catch (error_already_set &e) {
e.restore();
return;
} catch (const builtin_exception &e) {
e.set_error();
return;
} }
} }
#endif #endif
/// Return a reference to the current `internals` data /// Return a reference to the current `internals` data
PYBIND11_NOINLINE inline internals &get_internals() { PYBIND11_NOINLINE internals &get_internals() {
auto **&internals_pp = get_internals_pp(); auto **&internals_pp = get_internals_pp();
if (internals_pp && *internals_pp) if (internals_pp && *internals_pp) {
return **internals_pp; return **internals_pp;
}
// Ensure that the GIL is held since we will need to make Python calls. // Ensure that the GIL is held since we will need to make Python calls.
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals. // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
struct gil_scoped_acquire_local { struct gil_scoped_acquire_local {
gil_scoped_acquire_local() : state (PyGILState_Ensure()) {} gil_scoped_acquire_local() : state(PyGILState_Ensure()) {}
~gil_scoped_acquire_local() { PyGILState_Release(state); } ~gil_scoped_acquire_local() { PyGILState_Release(state); }
const PyGILState_STATE state; const PyGILState_STATE state;
} gil; } gil;
@ -282,26 +443,28 @@ PYBIND11_NOINLINE inline internals &get_internals() {
(*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
#endif #endif
} else { } else {
if (!internals_pp) internals_pp = new internals*(); if (!internals_pp) {
internals_pp = new internals *();
}
auto *&internals_ptr = *internals_pp; auto *&internals_ptr = *internals_pp;
internals_ptr = new internals(); internals_ptr = new internals();
#if defined(WITH_THREAD) #if defined(WITH_THREAD)
#if PY_VERSION_HEX < 0x03090000 # if PY_VERSION_HEX < 0x03090000
PyEval_InitThreads(); PyEval_InitThreads();
#endif # endif
PyThreadState *tstate = PyThreadState_Get(); PyThreadState *tstate = PyThreadState_Get();
#if PY_VERSION_HEX >= 0x03070000 if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
internals_ptr->tstate = PyThread_tss_alloc(); pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
if (!internals_ptr->tstate || (PyThread_tss_create(internals_ptr->tstate) != 0)) }
pybind11_fail("get_internals: could not successfully initialize the TSS key!"); PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
PyThread_tss_set(internals_ptr->tstate, tstate);
#else # if PYBIND11_INTERNALS_VERSION > 4
internals_ptr->tstate = PyThread_create_key(); if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
if (internals_ptr->tstate == -1) pybind11_fail("get_internals: could not successfully initialize the "
pybind11_fail("get_internals: could not successfully initialize the TLS key!"); "loader_life_support TSS key!");
PyThread_set_key_value(internals_ptr->tstate, tstate); }
#endif # endif
internals_ptr->istate = tstate->interp; internals_ptr->istate = tstate->interp;
#endif #endif
builtins[id] = capsule(internals_pp); builtins[id] = capsule(internals_pp);
@ -313,9 +476,53 @@ PYBIND11_NOINLINE inline internals &get_internals() {
return **internals_pp; return **internals_pp;
} }
/// Works like `internals.registered_types_cpp`, but for module-local registered types: // the internals struct (above) is shared between all the modules. local_internals are only
inline type_map<type_info *> &registered_local_types_cpp() { // for a single module. Any changes made to internals may require an update to
static type_map<type_info *> locals{}; // PYBIND11_INTERNALS_VERSION, breaking backwards compatibility. local_internals is, by design,
// restricted to a single module. Whether a module has local internals or not should not
// impact any other modules, because the only things accessing the local internals is the
// module that contains them.
struct local_internals {
type_map<type_info *> registered_types_cpp;
std::forward_list<ExceptionTranslator> registered_exception_translators;
#if defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
// For ABI compatibility, we can't store the loader_life_support TLS key in
// the `internals` struct directly. Instead, we store it in `shared_data` and
// cache a copy in `local_internals`. If we allocated a separate TLS key for
// each instance of `local_internals`, we could end up allocating hundreds of
// TLS keys if hundreds of different pybind11 modules are loaded (which is a
// plausible number).
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
// Holds the shared TLS key for the loader_life_support stack.
struct shared_loader_life_support_data {
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
shared_loader_life_support_data() {
if (!PYBIND11_TLS_KEY_CREATE(loader_life_support_tls_key)) {
pybind11_fail("local_internals: could not successfully initialize the "
"loader_life_support TLS key!");
}
}
// We can't help but leak the TLS key, because Python never unloads extension modules.
};
local_internals() {
auto &internals = get_internals();
// Get or create the `loader_life_support_stack_key`.
auto &ptr = internals.shared_data["_life_support"];
if (!ptr) {
ptr = new shared_loader_life_support_data;
}
loader_life_support_tls_key
= static_cast<shared_loader_life_support_data *>(ptr)->loader_life_support_tls_key;
}
#endif // defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
};
/// Works like `get_internals`, but for things which are locally registered.
inline local_internals &get_local_internals() {
static local_internals locals;
return locals; return locals;
} }
@ -335,14 +542,14 @@ PYBIND11_NAMESPACE_END(detail)
/// Returns a named pointer that is shared among all extension modules (using the same /// Returns a named pointer that is shared among all extension modules (using the same
/// pybind11 version) running in the current interpreter. Names starting with underscores /// pybind11 version) running in the current interpreter. Names starting with underscores
/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. /// are reserved for internal usage. Returns `nullptr` if no matching entry was found.
inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { PYBIND11_NOINLINE void *get_shared_data(const std::string &name) {
auto &internals = detail::get_internals(); auto &internals = detail::get_internals();
auto it = internals.shared_data.find(name); auto it = internals.shared_data.find(name);
return it != internals.shared_data.end() ? it->second : nullptr; return it != internals.shared_data.end() ? it->second : nullptr;
} }
/// Set the shared data that can be later recovered by `get_shared_data()`. /// Set the shared data that can be later recovered by `get_shared_data()`.
inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
detail::get_internals().shared_data[name] = data; detail::get_internals().shared_data[name] = data;
return data; return data;
} }
@ -350,7 +557,7 @@ inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *da
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if /// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if
/// such entry exists. Otherwise, a new object of default-constructible type `T` is /// such entry exists. Otherwise, a new object of default-constructible type `T` is
/// added to the shared data under the given name and a reference to it is returned. /// added to the shared data under the given name and a reference to it is returned.
template<typename T> template <typename T>
T &get_or_create_shared_data(const std::string &name) { T &get_or_create_shared_data(const std::string &name) {
auto &internals = detail::get_internals(); auto &internals = detail::get_internals();
auto it = internals.shared_data.find(name); auto it = internals.shared_data.find(name);

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@
#include <cstdlib> #include <cstdlib>
#if defined(__GNUG__) #if defined(__GNUG__)
#include <cxxabi.h> # include <cxxabi.h>
#endif #endif
#include "common.h" #include "common.h"
@ -24,18 +24,21 @@ PYBIND11_NAMESPACE_BEGIN(detail)
inline void erase_all(std::string &string, const std::string &search) { inline void erase_all(std::string &string, const std::string &search) {
for (size_t pos = 0;;) { for (size_t pos = 0;;) {
pos = string.find(search, pos); pos = string.find(search, pos);
if (pos == std::string::npos) break; if (pos == std::string::npos) {
break;
}
string.erase(pos, search.length()); string.erase(pos, search.length());
} }
} }
PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { PYBIND11_NOINLINE void clean_type_id(std::string &name) {
#if defined(__GNUG__) #if defined(__GNUG__)
int status = 0; int status = 0;
std::unique_ptr<char, void (*)(void *)> res { std::unique_ptr<char, void (*)(void *)> res{
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free};
if (status == 0) if (status == 0) {
name = res.get(); name = res.get();
}
#else #else
detail::erase_all(name, "class "); detail::erase_all(name, "class ");
detail::erase_all(name, "struct "); detail::erase_all(name, "struct ");
@ -46,7 +49,8 @@ PYBIND11_NOINLINE inline void clean_type_id(std::string &name) {
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/// Return a string representation of a C++ type /// Return a string representation of a C++ type
template <typename T> static std::string type_id() { template <typename T>
static std::string type_id() {
std::string name(typeid(T).name()); std::string name(typeid(T).name());
detail::clean_type_id(name); detail::clean_type_id(name);
return name; return name;

View File

@ -9,164 +9,191 @@
#pragma once #pragma once
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
See also:
https://stackoverflow.com/questions/2579576/i-dir-vs-isystem-dir
https://stackoverflow.com/questions/1741816/isystem-for-ms-visual-studio-c-compiler
*/
#include "numpy.h" #include "numpy.h"
#if defined(__INTEL_COMPILER) // The C4127 suppression was introduced for Eigen 3.4.0. In theory we could
# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) // make it version specific, or even remove it later, but considering that
#elif defined(__GNUG__) || defined(__clang__) // 1. C4127 is generally far more distracting than useful for modern template code, and
# pragma GCC diagnostic push // 2. we definitely want to ignore any MSVC warnings originating from Eigen code,
# pragma GCC diagnostic ignored "-Wconversion" // it is probably best to keep this around indefinitely.
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
# ifdef __clang__
// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated
// under Clang, so disable that warning here:
# pragma GCC diagnostic ignored "-Wdeprecated"
# endif
# if __GNUC__ >= 7
# pragma GCC diagnostic ignored "-Wint-in-bool-context"
# endif
#endif
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant # pragma warning(disable : 4127) // C4127: conditional expression is constant
# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17
#endif #endif
#include <Eigen/Core> #include <Eigen/Core>
#include <Eigen/SparseCore> #include <Eigen/SparseCore>
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit // Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit
// move constructors that break things. We could detect this an explicitly copy, but an extra copy // move constructors that break things. We could detect this an explicitly copy, but an extra copy
// of matrices seems highly undesirable. // of matrices seems highly undesirable.
static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); static_assert(EIGEN_VERSION_AT_LEAST(3, 2, 7),
"Eigen support in pybind11 requires Eigen >= 3.2.7");
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: // Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>; using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
template <typename MatrixType> using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>; template <typename MatrixType>
template <typename MatrixType> using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>; using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
template <typename MatrixType>
using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if EIGEN_VERSION_AT_LEAST(3,3,0) #if EIGEN_VERSION_AT_LEAST(3, 3, 0)
using EigenIndex = Eigen::Index; using EigenIndex = Eigen::Index;
template <typename Scalar, int Flags, typename StorageIndex>
using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
#else #else
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
template <typename Scalar, int Flags, typename StorageIndex>
using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
#endif #endif
// Matches Eigen::Map, Eigen::Ref, blocks, etc: // Matches Eigen::Map, Eigen::Ref, blocks, etc:
template <typename T> using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>, std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>; template <typename T>
template <typename T> using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>; using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>,
template <typename T> using is_eigen_dense_plain = all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>; std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
template <typename T> using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>; template <typename T>
using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>;
template <typename T>
using is_eigen_dense_plain
= all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>;
template <typename T>
using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>;
// Test for objects inheriting from EigenBase<Derived> that aren't captured by the above. This // Test for objects inheriting from EigenBase<Derived> that aren't captured by the above. This
// basically covers anything that can be assigned to a dense matrix but that don't have a typical // basically covers anything that can be assigned to a dense matrix but that don't have a typical
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and // matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
// SelfAdjointView fall into this category. // SelfAdjointView fall into this category.
template <typename T> using is_eigen_other = all_of< template <typename T>
is_template_base_of<Eigen::EigenBase, T>, using is_eigen_other
negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>> = all_of<is_template_base_of<Eigen::EigenBase, T>,
>; negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>>;
// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): // Captures numpy/eigen conformability status (returned by EigenProps::conformable()):
template <bool EigenRowMajor> struct EigenConformable { template <bool EigenRowMajor>
struct EigenConformable {
bool conformable = false; bool conformable = false;
EigenIndex rows = 0, cols = 0; EigenIndex rows = 0, cols = 0;
EigenDStride stride{0, 0}; // Only valid if negativestrides is false! EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
bool negativestrides = false; // If true, do not use stride! bool negativestrides = false; // If true, do not use stride!
// NOLINTNEXTLINE(google-explicit-constructor)
EigenConformable(bool fits = false) : conformable{fits} {} EigenConformable(bool fits = false) : conformable{fits} {}
// Matrix type: // Matrix type:
EigenConformable(EigenIndex r, EigenIndex c, EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride)
EigenIndex rstride, EigenIndex cstride) : : conformable{true}, rows{r}, cols{c},
conformable{true}, rows{r}, cols{c} { // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
// TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 // http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
if (rstride < 0 || cstride < 0) { stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
negativestrides = true; : (cstride > 0 ? cstride : 0) /* outer stride */,
} else { EigenRowMajor ? (cstride > 0 ? cstride : 0)
stride = {EigenRowMajor ? rstride : cstride /* outer stride */, : (rstride > 0 ? rstride : 0) /* inner stride */},
EigenRowMajor ? cstride : rstride /* inner stride */ }; negativestrides{rstride < 0 || cstride < 0} {}
}
}
// Vector type: // Vector type:
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride)
: EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} : EigenConformable(r, c, r == 1 ? c * stride : stride, c == 1 ? r : r * stride) {}
template <typename props> bool stride_compatible() const { template <typename props>
bool stride_compatible() const {
// To have compatible strides, we need (on both dimensions) one of fully dynamic strides, // To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
// matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) // matching strides, or a dimension size of 1 (in which case the stride value is
return // irrelevant)
!negativestrides && return !negativestrides
(props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || && (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
(EigenRowMajor ? cols : rows) == 1) && || (EigenRowMajor ? cols : rows) == 1)
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || && (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
(EigenRowMajor ? rows : cols) == 1); || (EigenRowMajor ? rows : cols) == 1);
} }
// NOLINTNEXTLINE(google-explicit-constructor)
operator bool() const { return conformable; } operator bool() const { return conformable; }
}; };
template <typename Type> struct eigen_extract_stride { using type = Type; }; template <typename Type>
struct eigen_extract_stride {
using type = Type;
};
template <typename PlainObjectType, int MapOptions, typename StrideType> template <typename PlainObjectType, int MapOptions, typename StrideType>
struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> { using type = StrideType; }; struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> {
using type = StrideType;
};
template <typename PlainObjectType, int Options, typename StrideType> template <typename PlainObjectType, int Options, typename StrideType>
struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> { using type = StrideType; }; struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> {
using type = StrideType;
};
// Helper struct for extracting information from an Eigen type // Helper struct for extracting information from an Eigen type
template <typename Type_> struct EigenProps { template <typename Type_>
struct EigenProps {
using Type = Type_; using Type = Type_;
using Scalar = typename Type::Scalar; using Scalar = typename Type::Scalar;
using StrideType = typename eigen_extract_stride<Type>::type; using StrideType = typename eigen_extract_stride<Type>::type;
static constexpr EigenIndex static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
rows = Type::RowsAtCompileTime, size = Type::SizeAtCompileTime;
cols = Type::ColsAtCompileTime, static constexpr bool row_major = Type::IsRowMajor,
size = Type::SizeAtCompileTime; vector
static constexpr bool = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
row_major = Type::IsRowMajor, fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic,
vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 fixed = size != Eigen::Dynamic, // Fully-fixed size
fixed_rows = rows != Eigen::Dynamic, dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
fixed_cols = cols != Eigen::Dynamic,
fixed = size != Eigen::Dynamic, // Fully-fixed size
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
template <EigenIndex i, EigenIndex ifzero> using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>; template <EigenIndex i, EigenIndex ifzero>
static constexpr EigenIndex inner_stride = if_zero<StrideType::InnerStrideAtCompileTime, 1>::value, using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
outer_stride = if_zero<StrideType::OuterStrideAtCompileTime, static constexpr EigenIndex inner_stride
vector ? size : row_major ? cols : rows>::value; = if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; vector ? size
static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; : row_major ? cols
: rows > ::value;
static constexpr bool dynamic_stride
= inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic;
static constexpr bool requires_row_major
= !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1;
static constexpr bool requires_col_major
= !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1;
// Takes an input array and determines whether we can make it fit into the Eigen type. If // Takes an input array and determines whether we can make it fit into the Eigen type. If
// the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector
// (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type).
static EigenConformable<row_major> conformable(const array &a) { static EigenConformable<row_major> conformable(const array &a) {
const auto dims = a.ndim(); const auto dims = a.ndim();
if (dims < 1 || dims > 2) if (dims < 1 || dims > 2) {
return false; return false;
}
if (dims == 2) { // Matrix type: require exact match (or dynamic) if (dims == 2) { // Matrix type: require exact match (or dynamic)
EigenIndex EigenIndex np_rows = a.shape(0), np_cols = a.shape(1),
np_rows = a.shape(0), np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
np_cols = a.shape(1), np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)), if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows)
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar)); || (PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols)) {
if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols))
return false; return false;
}
return {np_rows, np_cols, np_rstride, np_cstride}; return {np_rows, np_cols, np_rstride, np_cstride};
} }
// Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever // Otherwise we're storing an n-vector. Only one of the strides will be used, but
// is used, we want the (single) numpy stride value. // whichever is used, we want the (single) numpy stride value.
const EigenIndex n = a.shape(0), const EigenIndex n = a.shape(0),
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)); stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
if (vector) { // Eigen type is a compile-time vector if (vector) { // Eigen type is a compile-time vector
if (fixed && size != n) if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n) {
return false; // Vector size mismatch return false; // Vector size mismatch
}
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
} }
if (fixed) { if (fixed) {
@ -176,48 +203,59 @@ template <typename Type_> struct EigenProps {
if (fixed_cols) { if (fixed_cols) {
// Since this isn't a vector, cols must be != 1. We allow this only if it exactly // Since this isn't a vector, cols must be != 1. We allow this only if it exactly
// equals the number of elements (rows is Dynamic, and so 1 row is allowed). // equals the number of elements (rows is Dynamic, and so 1 row is allowed).
if (cols != n) return false; if (cols != n) {
return false;
}
return {1, n, stride}; return {1, n, stride};
} // Otherwise it's either fully dynamic, or column dynamic; both become a column vector } // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
if (fixed_rows && rows != n) return false; if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) {
return {n, 1, stride}; return false;
}
return {n, 1, stride};
} }
static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value; static constexpr bool show_writeable
= is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
static constexpr bool show_order = is_eigen_dense_map<Type>::value; static constexpr bool show_order = is_eigen_dense_map<Type>::value;
static constexpr bool show_c_contiguous = show_order && requires_row_major; static constexpr bool show_c_contiguous = show_order && requires_row_major;
static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; static constexpr bool show_f_contiguous
= !show_c_contiguous && show_order && requires_col_major;
static constexpr auto descriptor = static constexpr auto descriptor
_x("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + = const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
_x("[") + _x<fixed_rows>(_x<(size_t) rows>(), _x("m")) + + const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
_x(", ") + _x<fixed_cols>(_x<(size_t) cols>(), _x("n")) + + const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]")
_x("]") + +
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to be // For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
// satisfied: writeable=True (for a mutable reference), and, depending on the map's stride // be satisfied: writeable=True (for a mutable reference), and, depending on the map's
// options, possibly f_contiguous or c_contiguous. We include them in the descriptor output // stride options, possibly f_contiguous or c_contiguous. We include them in the
// to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to // descriptor output to provide some hint as to why a TypeError is occurring (otherwise
// see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you // it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and
// *gave* a numpy.ndarray of the right type and dimensions. // an error message that you *gave* a numpy.ndarray of the right type and dimensions.
_x<show_writeable>(", flags.writeable", "") + const_name<show_writeable>(", flags.writeable", "")
_x<show_c_contiguous>(", flags.c_contiguous", "") + + const_name<show_c_contiguous>(", flags.c_contiguous", "")
_x<show_f_contiguous>(", flags.f_contiguous", "") + + const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]");
_x("]");
}; };
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, // Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. // otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
template <typename props> handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { template <typename props>
handle
eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
constexpr ssize_t elem_size = sizeof(typename props::Scalar); constexpr ssize_t elem_size = sizeof(typename props::Scalar);
array a; array a;
if (props::vector) if (props::vector) {
a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base);
else } else {
a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, a = array({src.rows(), src.cols()},
src.data(), base); {elem_size * src.rowStride(), elem_size * src.colStride()},
src.data(),
base);
}
if (!writeable) if (!writeable) {
array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
}
return a.release(); return a.release();
} }
@ -233,10 +271,10 @@ handle eigen_ref_array(Type &src, handle parent = none()) {
return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value); return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value);
} }
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy // Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
// array that references the encapsulated data with a python-side reference to the capsule to tie // numpy array that references the encapsulated data with a python-side reference to the capsule to
// its destruction to that of any dependent python objects. Const-ness is determined by whether or // tie its destruction to that of any dependent python objects. Const-ness is determined by
// not the Type of the pointer given is const. // whether or not the Type of the pointer given is const.
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>> template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
handle eigen_encapsulate(Type *src) { handle eigen_encapsulate(Type *src) {
capsule base(src, [](void *o) { delete static_cast<Type *>(o); }); capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
@ -245,35 +283,42 @@ handle eigen_encapsulate(Type *src) {
// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense // Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense
// types. // types.
template<typename Type> template <typename Type>
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> { struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
using Scalar = typename Type::Scalar; using Scalar = typename Type::Scalar;
using props = EigenProps<Type>; using props = EigenProps<Type>;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
// If we're in no-convert mode, only load if given an array of the correct type // If we're in no-convert mode, only load if given an array of the correct type
if (!convert && !isinstance<array_t<Scalar>>(src)) if (!convert && !isinstance<array_t<Scalar>>(src)) {
return false; return false;
}
// Coerce into an array, but don't do type conversion yet; the copy below handles it. // Coerce into an array, but don't do type conversion yet; the copy below handles it.
auto buf = array::ensure(src); auto buf = array::ensure(src);
if (!buf) if (!buf) {
return false; return false;
}
auto dims = buf.ndim(); auto dims = buf.ndim();
if (dims < 1 || dims > 2) if (dims < 1 || dims > 2) {
return false; return false;
}
auto fits = props::conformable(buf); auto fits = props::conformable(buf);
if (!fits) if (!fits) {
return false; return false;
}
// Allocate the new type, then build a numpy reference into it // Allocate the new type, then build a numpy reference into it
value = Type(fits.rows, fits.cols); value = Type(fits.rows, fits.cols);
auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value)); auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
if (dims == 1) ref = ref.squeeze(); if (dims == 1) {
else if (ref.ndim() == 1) buf = buf.squeeze(); ref = ref.squeeze();
} else if (ref.ndim() == 1) {
buf = buf.squeeze();
}
int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr());
@ -286,7 +331,6 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
} }
private: private:
// Cast implementation // Cast implementation
template <typename CType> template <typename CType>
static handle cast_impl(CType *src, return_value_policy policy, handle parent) { static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
@ -309,7 +353,6 @@ private:
} }
public: public:
// Normal returned non-reference, non-const value: // Normal returned non-reference, non-const value:
static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { static handle cast(Type &&src, return_value_policy /* policy */, handle parent) {
return cast_impl(&src, return_value_policy::move, parent); return cast_impl(&src, return_value_policy::move, parent);
@ -320,14 +363,18 @@ public:
} }
// lvalue reference return; default (automatic) becomes copy // lvalue reference return; default (automatic) becomes copy
static handle cast(Type &src, return_value_policy policy, handle parent) { static handle cast(Type &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) if (policy == return_value_policy::automatic
|| policy == return_value_policy::automatic_reference) {
policy = return_value_policy::copy; policy = return_value_policy::copy;
}
return cast_impl(&src, policy, parent); return cast_impl(&src, policy, parent);
} }
// const lvalue reference return; default (automatic) becomes copy // const lvalue reference return; default (automatic) becomes copy
static handle cast(const Type &src, return_value_policy policy, handle parent) { static handle cast(const Type &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) if (policy == return_value_policy::automatic
|| policy == return_value_policy::automatic_reference) {
policy = return_value_policy::copy; policy = return_value_policy::copy;
}
return cast(&src, policy, parent); return cast(&src, policy, parent);
} }
// non-const pointer return // non-const pointer return
@ -341,28 +388,32 @@ public:
static constexpr auto name = props::descriptor; static constexpr auto name = props::descriptor;
operator Type*() { return &value; } // NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return value; } operator Type *() { return &value; }
operator Type&&() && { return std::move(value); } // NOLINTNEXTLINE(google-explicit-constructor)
template <typename T> using cast_op_type = movable_cast_op_type<T>; operator Type &() { return value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type &&() && { return std::move(value); }
template <typename T>
using cast_op_type = movable_cast_op_type<T>;
private: private:
Type value; Type value;
}; };
// Base class for casting reference/map/block/etc. objects back to python. // Base class for casting reference/map/block/etc. objects back to python.
template <typename MapType> struct eigen_map_caster { template <typename MapType>
struct eigen_map_caster {
private: private:
using props = EigenProps<MapType>; using props = EigenProps<MapType>;
public: public:
// Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has
// to stay around), but we'll allow it under the assumption that you know what you're doing (and // to stay around), but we'll allow it under the assumption that you know what you're doing
// have an appropriate keep_alive in place). We return a numpy array pointing directly at the // (and have an appropriate keep_alive in place). We return a numpy array pointing directly at
// ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note // the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
// that this means you need to ensure you don't destroy the object in some other way (e.g. with // Note that this means you need to ensure you don't destroy the object in some other way (e.g.
// an appropriate keep_alive, or with a reference to a statically allocated matrix). // with an appropriate keep_alive, or with a reference to a statically allocated matrix).
static handle cast(const MapType &src, return_value_policy policy, handle parent) { static handle cast(const MapType &src, return_value_policy policy, handle parent) {
switch (policy) { switch (policy) {
case return_value_policy::copy: case return_value_policy::copy:
@ -386,43 +437,50 @@ public:
// you end up here if you try anyway. // you end up here if you try anyway.
bool load(handle, bool) = delete; bool load(handle, bool) = delete;
operator MapType() = delete; operator MapType() = delete;
template <typename> using cast_op_type = MapType; template <typename>
using cast_op_type = MapType;
}; };
// We can return any map-like object (but can only load Refs, specialized next): // We can return any map-like object (but can only load Refs, specialized next):
template <typename Type> struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> template <typename Type>
: eigen_map_caster<Type> {}; struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> : eigen_map_caster<Type> {};
// Loader for Ref<...> arguments. See the documentation for info on how to make this work without // Loader for Ref<...> arguments. See the documentation for info on how to make this work without
// copying (it requires some extra effort in many cases). // copying (it requires some extra effort in many cases).
template <typename PlainObjectType, typename StrideType> template <typename PlainObjectType, typename StrideType>
struct type_caster< struct type_caster<
Eigen::Ref<PlainObjectType, 0, StrideType>, Eigen::Ref<PlainObjectType, 0, StrideType>,
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value> enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
> : public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> { : public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
private: private:
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>; using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
using props = EigenProps<Type>; using props = EigenProps<Type>;
using Scalar = typename props::Scalar; using Scalar = typename props::Scalar;
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>; using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
using Array = array_t<Scalar, array::forcecast | using Array
((props::row_major ? props::inner_stride : props::outer_stride) == 1 ? array::c_style : = array_t<Scalar,
(props::row_major ? props::outer_stride : props::inner_stride) == 1 ? array::f_style : 0)>; array::forcecast
| ((props::row_major ? props::inner_stride : props::outer_stride) == 1
? array::c_style
: (props::row_major ? props::outer_stride : props::inner_stride) == 1
? array::f_style
: 0)>;
static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value; static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value;
// Delay construction (these have no default constructor) // Delay construction (these have no default constructor)
std::unique_ptr<MapType> map; std::unique_ptr<MapType> map;
std::unique_ptr<Type> ref; std::unique_ptr<Type> ref;
// Our array. When possible, this is just a numpy array pointing to the source data, but // Our array. When possible, this is just a numpy array pointing to the source data, but
// sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
// layout, or is an array of a type that needs to be converted). Using a numpy temporary // incompatible layout, or is an array of a type that needs to be converted). Using a numpy
// (rather than an Eigen temporary) saves an extra copy when we need both type conversion and // temporary (rather than an Eigen temporary) saves an extra copy when we need both type
// storage order conversion. (Note that we refuse to use this temporary copy when loading an // conversion and storage order conversion. (Note that we refuse to use this temporary copy
// argument for a Ref<M> with M non-const, i.e. a read-write reference). // when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
Array copy_or_ref; Array copy_or_ref;
public: public:
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
// First check whether what we have is already an array of the right type. If not, we can't // First check whether what we have is already an array of the right type. If not, we
// avoid a copy (because the copy is also going to do type conversion). // can't avoid a copy (because the copy is also going to do type conversion).
bool need_copy = !isinstance<Array>(src); bool need_copy = !isinstance<Array>(src);
EigenConformable<props::row_major> fits; EigenConformable<props::row_major> fits;
@ -433,13 +491,15 @@ public:
if (aref && (!need_writeable || aref.writeable())) { if (aref && (!need_writeable || aref.writeable())) {
fits = props::conformable(aref); fits = props::conformable(aref);
if (!fits) return false; // Incompatible dimensions if (!fits) {
if (!fits.template stride_compatible<props>()) return false; // Incompatible dimensions
}
if (!fits.template stride_compatible<props>()) {
need_copy = true; need_copy = true;
else } else {
copy_or_ref = std::move(aref); copy_or_ref = std::move(aref);
} }
else { } else {
need_copy = true; need_copy = true;
} }
} }
@ -448,64 +508,93 @@ public:
// We need to copy: If we need a mutable reference, or we're not supposed to convert // We need to copy: If we need a mutable reference, or we're not supposed to convert
// (either because we're in the no-convert overload pass, or because we're explicitly // (either because we're in the no-convert overload pass, or because we're explicitly
// instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading.
if (!convert || need_writeable) return false; if (!convert || need_writeable) {
return false;
}
Array copy = Array::ensure(src); Array copy = Array::ensure(src);
if (!copy) return false; if (!copy) {
fits = props::conformable(copy);
if (!fits || !fits.template stride_compatible<props>())
return false; return false;
}
fits = props::conformable(copy);
if (!fits || !fits.template stride_compatible<props>()) {
return false;
}
copy_or_ref = std::move(copy); copy_or_ref = std::move(copy);
loader_life_support::add_patient(copy_or_ref); loader_life_support::add_patient(copy_or_ref);
} }
ref.reset(); ref.reset();
map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); map.reset(new MapType(data(copy_or_ref),
fits.rows,
fits.cols,
make_stride(fits.stride.outer(), fits.stride.inner())));
ref.reset(new Type(*map)); ref.reset(new Type(*map));
return true; return true;
} }
operator Type*() { return ref.get(); } // NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return *ref; } operator Type *() { return ref.get(); }
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>; // NOLINTNEXTLINE(google-explicit-constructor)
operator Type &() { return *ref; }
template <typename _T>
using cast_op_type = pybind11::detail::cast_op_type<_T>;
private: private:
template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0> template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0>
Scalar *data(Array &a) { return a.mutable_data(); } Scalar *data(Array &a) {
return a.mutable_data();
}
template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0> template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0>
const Scalar *data(Array &a) { return a.data(); } const Scalar *data(Array &a) {
return a.data();
}
// Attempt to figure out a constructor of `Stride` that will work. // Attempt to figure out a constructor of `Stride` that will work.
// If both strides are fixed, use a default constructor: // If both strides are fixed, use a default constructor:
template <typename S> using stride_ctor_default = bool_constant< template <typename S>
S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
std::is_default_constructible<S>::value>; && S::OuterStrideAtCompileTime != Eigen::Dynamic
&& std::is_default_constructible<S>::value>;
// Otherwise, if there is a two-index constructor, assume it is (outer,inner) like // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
// Eigen::Stride, and use it: // Eigen::Stride, and use it:
template <typename S> using stride_ctor_dual = bool_constant< template <typename S>
!stride_ctor_default<S>::value && std::is_constructible<S, EigenIndex, EigenIndex>::value>; using stride_ctor_dual
= bool_constant<!stride_ctor_default<S>::value
&& std::is_constructible<S, EigenIndex, EigenIndex>::value>;
// Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use
// it (passing whichever stride is dynamic). // it (passing whichever stride is dynamic).
template <typename S> using stride_ctor_outer = bool_constant< template <typename S>
!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value && using stride_ctor_outer
S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && = bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
std::is_constructible<S, EigenIndex>::value>; && S::OuterStrideAtCompileTime == Eigen::Dynamic
template <typename S> using stride_ctor_inner = bool_constant< && S::InnerStrideAtCompileTime != Eigen::Dynamic
!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value && && std::is_constructible<S, EigenIndex>::value>;
S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && template <typename S>
std::is_constructible<S, EigenIndex>::value>; using stride_ctor_inner
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
&& S::InnerStrideAtCompileTime == Eigen::Dynamic
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
&& std::is_constructible<S, EigenIndex>::value>;
template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0> template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0>
static S make_stride(EigenIndex, EigenIndex) { return S(); } static S make_stride(EigenIndex, EigenIndex) {
return S();
}
template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0> template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0>
static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } static S make_stride(EigenIndex outer, EigenIndex inner) {
return S(outer, inner);
}
template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0> template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0>
static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } static S make_stride(EigenIndex outer, EigenIndex) {
return S(outer);
}
template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0> template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0>
static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } static S make_stride(EigenIndex, EigenIndex inner) {
return S(inner);
}
}; };
// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not // type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not
@ -515,14 +604,18 @@ private:
template <typename Type> template <typename Type>
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> { struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
protected: protected:
using Matrix = Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>; using Matrix
= Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
using props = EigenProps<Matrix>; using props = EigenProps<Matrix>;
public: public:
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
handle h = eigen_encapsulate<props>(new Matrix(src)); handle h = eigen_encapsulate<props>(new Matrix(src));
return h; return h;
} }
static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } static handle cast(const Type *src, return_value_policy policy, handle parent) {
return cast(*src, policy, parent);
}
static constexpr auto name = props::descriptor; static constexpr auto name = props::descriptor;
@ -531,10 +624,11 @@ public:
// you end up here if you try anyway. // you end up here if you try anyway.
bool load(handle, bool) = delete; bool load(handle, bool) = delete;
operator Type() = delete; operator Type() = delete;
template <typename> using cast_op_type = Type; template <typename>
using cast_op_type = Type;
}; };
template<typename Type> template <typename Type>
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> { struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
using Scalar = typename Type::Scalar; using Scalar = typename Type::Scalar;
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>; using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
@ -542,13 +636,13 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
static constexpr bool rowMajor = Type::IsRowMajor; static constexpr bool rowMajor = Type::IsRowMajor;
bool load(handle src, bool) { bool load(handle src, bool) {
if (!src) if (!src) {
return false; return false;
}
auto obj = reinterpret_borrow<object>(src); auto obj = reinterpret_borrow<object>(src);
object sparse_module = module_::import("scipy.sparse"); object sparse_module = module_::import("scipy.sparse");
object matrix_type = sparse_module.attr( object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
rowMajor ? "csr_matrix" : "csc_matrix");
if (!type::handle_of(obj).is(matrix_type)) { if (!type::handle_of(obj).is(matrix_type)) {
try { try {
@ -564,41 +658,42 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
auto nnz = obj.attr("nnz").cast<Index>(); auto nnz = obj.attr("nnz").cast<Index>();
if (!values || !innerIndices || !outerIndices) if (!values || !innerIndices || !outerIndices) {
return false; return false;
}
value = Eigen::MappedSparseMatrix<Scalar, Type::Flags, StorageIndex>( value = EigenMapSparseMatrix<Scalar,
shape[0].cast<Index>(), shape[1].cast<Index>(), nnz, Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); StorageIndex>(shape[0].cast<Index>(),
shape[1].cast<Index>(),
nnz,
outerIndices.mutable_data(),
innerIndices.mutable_data(),
values.mutable_data());
return true; return true;
} }
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
const_cast<Type&>(src).makeCompressed(); const_cast<Type &>(src).makeCompressed();
object matrix_type = module_::import("scipy.sparse").attr( object matrix_type
rowMajor ? "csr_matrix" : "csc_matrix"); = module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
array data(src.nonZeros(), src.valuePtr()); array data(src.nonZeros(), src.valuePtr());
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
array innerIndices(src.nonZeros(), src.innerIndexPtr()); array innerIndices(src.nonZeros(), src.innerIndexPtr());
return matrix_type( return matrix_type(std::make_tuple(data, innerIndices, outerIndices),
std::make_tuple(data, innerIndices, outerIndices), std::make_pair(src.rows(), src.cols()))
std::make_pair(src.rows(), src.cols()) .release();
).release();
} }
PYBIND11_TYPE_CASTER(Type, _x<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") PYBIND11_TYPE_CASTER(Type,
+ npy_format_descriptor<Scalar>::name + _x("]")); const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
"scipy.sparse.csc_matrix[")
+ npy_format_descriptor<Scalar>::name + const_name("]"));
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(__GNUG__) || defined(__clang__)
# pragma GCC diagnostic pop
#elif defined(_MSC_VER)
# pragma warning(pop)
#endif

View File

@ -12,22 +12,21 @@
#include "pybind11.h" #include "pybind11.h"
#include "eval.h" #include "eval.h"
#include <memory>
#include <vector>
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
# error Embedding the interpreter is not supported with PyPy # error Embedding the interpreter is not supported with PyPy
#endif #endif
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ # define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
extern "C" PyObject *pybind11_init_impl_##name(); \ extern "C" PyObject *pybind11_init_impl_##name(); \
extern "C" PyObject *pybind11_init_impl_##name() { \ extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); }
return pybind11_init_wrapper_##name(); \
}
#else #else
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ # define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
extern "C" void pybind11_init_impl_##name(); \ extern "C" void pybind11_init_impl_##name(); \
extern "C" void pybind11_init_impl_##name() { \ extern "C" void pybind11_init_impl_##name() { pybind11_init_wrapper_##name(); }
pybind11_init_wrapper_##name(); \
}
#endif #endif
/** \rst /** \rst
@ -45,25 +44,23 @@
}); });
} }
\endrst */ \endrst */
#define PYBIND11_EMBEDDED_MODULE(name, variable) \ #define PYBIND11_EMBEDDED_MODULE(name, variable) \
static ::pybind11::module_::module_def \ static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
PYBIND11_CONCAT(pybind11_module_def_, name); \ static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \ static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ auto m = ::pybind11::module_::create_extension_module( \
auto m = ::pybind11::module_::create_extension_module( \ PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
PYBIND11_TOSTRING(name), nullptr, \ try { \
&PYBIND11_CONCAT(pybind11_module_def_, name)); \ PYBIND11_CONCAT(pybind11_init_, name)(m); \
try { \ return m.ptr(); \
PYBIND11_CONCAT(pybind11_init_, name)(m); \ } \
return m.ptr(); \ PYBIND11_CATCH_INIT_EXCEPTIONS \
} PYBIND11_CATCH_INIT_EXCEPTIONS \ } \
} \ PYBIND11_EMBEDDED_MODULE_IMPL(name) \
PYBIND11_EMBEDDED_MODULE_IMPL(name) \ ::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \ PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \
(PYBIND11_TOSTRING(name), \ void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \
PYBIND11_CONCAT(pybind11_init_impl_, name)); \ & variable) // NOLINT(bugprone-macro-parentheses)
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
@ -71,43 +68,136 @@ PYBIND11_NAMESPACE_BEGIN(detail)
/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. /// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
struct embedded_module { struct embedded_module {
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
using init_t = PyObject *(*)(); using init_t = PyObject *(*) ();
#else #else
using init_t = void (*)(); using init_t = void (*)();
#endif #endif
embedded_module(const char *name, init_t init) { embedded_module(const char *name, init_t init) {
if (Py_IsInitialized() != 0) if (Py_IsInitialized() != 0) {
pybind11_fail("Can't add new modules after the interpreter has been initialized"); pybind11_fail("Can't add new modules after the interpreter has been initialized");
}
auto result = PyImport_AppendInittab(name, init); auto result = PyImport_AppendInittab(name, init);
if (result == -1) if (result == -1) {
pybind11_fail("Insufficient memory to add a new module"); pybind11_fail("Insufficient memory to add a new module");
}
} }
}; };
struct wide_char_arg_deleter {
void operator()(wchar_t *ptr) const {
#if PY_VERSION_HEX >= 0x030500f0
// API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
PyMem_RawFree(ptr);
#else
delete[] ptr;
#endif
}
};
inline wchar_t *widen_chars(const char *safe_arg) {
#if PY_VERSION_HEX >= 0x030500f0
wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr);
#else
wchar_t *widened_arg = nullptr;
// warning C4996: 'mbstowcs': This function or variable may be unsafe.
# if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4996)
# endif
# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS
size_t count = std::strlen(safe_arg);
# else
size_t count = std::mbstowcs(nullptr, safe_arg, 0);
# endif
if (count != static_cast<size_t>(-1)) {
widened_arg = new wchar_t[count + 1];
std::mbstowcs(widened_arg, safe_arg, count + 1);
}
# if defined(_MSC_VER)
# pragma warning(pop)
# endif
#endif
return widened_arg;
}
/// Python 2.x/3.x-compatible version of `PySys_SetArgv`
inline void set_interpreter_argv(int argc, const char *const *argv, bool add_program_dir_to_path) {
// Before it was special-cased in python 3.8, passing an empty or null argv
// caused a segfault, so we have to reimplement the special case ourselves.
bool special_case = (argv == nullptr || argc <= 0);
const char *const empty_argv[]{"\0"};
const char *const *safe_argv = special_case ? empty_argv : argv;
if (special_case) {
argc = 1;
}
auto argv_size = static_cast<size_t>(argc);
#if PY_MAJOR_VERSION >= 3
// SetArgv* on python 3 takes wchar_t, so we have to convert.
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> widened_argv_entries;
widened_argv_entries.reserve(argv_size);
for (size_t ii = 0; ii < argv_size; ++ii) {
widened_argv_entries.emplace_back(widen_chars(safe_argv[ii]));
if (!widened_argv_entries.back()) {
// A null here indicates a character-encoding failure or the python
// interpreter out of memory. Give up.
return;
}
widened_argv[ii] = widened_argv_entries.back().get();
}
auto *pysys_argv = widened_argv.get();
#else
// python 2.x
std::vector<std::string> strings{safe_argv, safe_argv + argv_size};
std::vector<char *> char_strings{argv_size};
for (std::size_t i = 0; i < argv_size; ++i)
char_strings[i] = &strings[i][0];
char **pysys_argv = char_strings.data();
#endif
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
}
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/** \rst /** \rst
Initialize the Python interpreter. No other pybind11 or CPython API functions can be Initialize the Python interpreter. No other pybind11 or CPython API functions can be
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
optional parameter can be used to skip the registration of signal handlers (see the optional `init_signal_handlers` parameter can be used to skip the registration of
`Python documentation`_ for details). Calling this function again after the interpreter signal handlers (see the `Python documentation`_ for details). Calling this function
has already been initialized is a fatal error. again after the interpreter has already been initialized is a fatal error.
If initializing the Python interpreter fails, then the program is terminated. (This If initializing the Python interpreter fails, then the program is terminated. (This
is controlled by the CPython runtime and is an exception to pybind11's normal behavior is controlled by the CPython runtime and is an exception to pybind11's normal behavior
of throwing exceptions on errors.) of throwing exceptions on errors.)
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
used to populate ``sys.argv`` and ``sys.path``.
See the |PySys_SetArgvEx documentation|_ for details.
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
\endrst */ \endrst */
inline void initialize_interpreter(bool init_signal_handlers = true) { inline void initialize_interpreter(bool init_signal_handlers = true,
if (Py_IsInitialized() != 0) int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
if (Py_IsInitialized() != 0) {
pybind11_fail("The interpreter is already running"); pybind11_fail("The interpreter is already running");
}
Py_InitializeEx(init_signal_handlers ? 1 : 0); Py_InitializeEx(init_signal_handlers ? 1 : 0);
// Make .py files in the working directory available by default detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
module_::import("sys").attr("path").cast<list>().append(".");
} }
/** \rst /** \rst
@ -154,8 +244,13 @@ inline void finalize_interpreter() {
// during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). // during destruction), so we get the pointer-pointer here and check it after Py_Finalize().
detail::internals **internals_ptr_ptr = detail::get_internals_pp(); detail::internals **internals_ptr_ptr = detail::get_internals_pp();
// It could also be stashed in builtins, so look there too: // It could also be stashed in builtins, so look there too:
if (builtins.contains(id) && isinstance<capsule>(builtins[id])) if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
internals_ptr_ptr = capsule(builtins[id]); internals_ptr_ptr = capsule(builtins[id]);
}
// Local internals contains data managed by the current interpreter, so we must clear them to
// avoid undefined behaviors when initializing another interpreter
detail::get_local_internals().registered_types_cpp.clear();
detail::get_local_internals().registered_exception_translators.clear();
Py_Finalize(); Py_Finalize();
@ -169,6 +264,8 @@ inline void finalize_interpreter() {
Scope guard version of `initialize_interpreter` and `finalize_interpreter`. Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
This a move-only guard and only a single instance can exist. This a move-only guard and only a single instance can exist.
See `initialize_interpreter` for a discussion of its constructor arguments.
.. code-block:: cpp .. code-block:: cpp
#include <pybind11/embed.h> #include <pybind11/embed.h>
@ -180,8 +277,11 @@ inline void finalize_interpreter() {
\endrst */ \endrst */
class scoped_interpreter { class scoped_interpreter {
public: public:
scoped_interpreter(bool init_signal_handlers = true) { explicit scoped_interpreter(bool init_signal_handlers = true,
initialize_interpreter(init_signal_handlers); int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
} }
scoped_interpreter(const scoped_interpreter &) = delete; scoped_interpreter(const scoped_interpreter &) = delete;
@ -190,8 +290,9 @@ public:
scoped_interpreter &operator=(scoped_interpreter &&) = delete; scoped_interpreter &operator=(scoped_interpreter &&) = delete;
~scoped_interpreter() { ~scoped_interpreter() {
if (is_valid) if (is_valid) {
finalize_interpreter(); finalize_interpreter();
}
} }
private: private:

View File

@ -1,5 +1,5 @@
/* /*
pybind11/exec.h: Support for evaluating Python expressions and statements pybind11/eval.h: Support for evaluating Python expressions and statements
from strings and files from strings and files
Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and
@ -11,24 +11,24 @@
#pragma once #pragma once
#include <utility>
#include "pybind11.h" #include "pybind11.h"
#include <utility>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
inline void ensure_builtins_in_globals(object &global) { inline void ensure_builtins_in_globals(object &global) {
#if PY_VERSION_HEX < 0x03080000 #if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000
// Running exec and eval on Python 2 and 3 adds `builtins` module under // Running exec and eval on Python 2 and 3 adds `builtins` module under
// `__builtins__` key to globals if not yet present. // `__builtins__` key to globals if not yet present.
// Python 3.8 made PyRun_String behave similarly. Let's also do that for // Python 3.8 made PyRun_String behave similarly. Let's also do that for
// older versions, for consistency. // older versions, for consistency. This was missing from PyPy3.8 7.3.7.
if (!global.contains("__builtins__")) if (!global.contains("__builtins__"))
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE); global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
#else #else
(void) global; (void) global;
#endif #endif
} }
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
@ -46,8 +46,9 @@ enum eval_mode {
template <eval_mode mode = eval_expr> template <eval_mode mode = eval_expr>
object eval(const str &expr, object global = globals(), object local = object()) { object eval(const str &expr, object global = globals(), object local = object()) {
if (!local) if (!local) {
local = global; local = global;
}
detail::ensure_builtins_in_globals(global); detail::ensure_builtins_in_globals(global);
@ -57,24 +58,31 @@ object eval(const str &expr, object global = globals(), object local = object())
int start = 0; int start = 0;
switch (mode) { switch (mode) {
case eval_expr: start = Py_eval_input; break; case eval_expr:
case eval_single_statement: start = Py_single_input; break; start = Py_eval_input;
case eval_statements: start = Py_file_input; break; break;
default: pybind11_fail("invalid evaluation mode"); case eval_single_statement:
start = Py_single_input;
break;
case eval_statements:
start = Py_file_input;
break;
default:
pybind11_fail("invalid evaluation mode");
} }
PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
if (!result) if (!result) {
throw error_already_set(); throw error_already_set();
}
return reinterpret_steal<object>(result); return reinterpret_steal<object>(result);
} }
template <eval_mode mode = eval_expr, size_t N> template <eval_mode mode = eval_expr, size_t N>
object eval(const char (&s)[N], object global = globals(), object local = object()) { object eval(const char (&s)[N], object global = globals(), object local = object()) {
/* Support raw string literals by removing common leading whitespace */ /* Support raw string literals by removing common leading whitespace */
auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) : str(s);
: str(s); return eval<mode>(expr, std::move(global), std::move(local));
return eval<mode>(expr, global, local);
} }
inline void exec(const str &expr, object global = globals(), object local = object()) { inline void exec(const str &expr, object global = globals(), object local = object()) {
@ -83,7 +91,7 @@ inline void exec(const str &expr, object global = globals(), object local = obje
template <size_t N> template <size_t N>
void exec(const char (&s)[N], object global = globals(), object local = object()) { void exec(const char (&s)[N], object global = globals(), object local = object()) {
eval<eval_statements>(s, global, local); eval<eval_statements>(s, std::move(global), std::move(local));
} }
#if defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03000000 #if defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03000000
@ -102,51 +110,67 @@ object eval_file(str) {
#else #else
template <eval_mode mode = eval_statements> template <eval_mode mode = eval_statements>
object eval_file(str fname, object global = globals(), object local = object()) { object eval_file(str fname, object global = globals(), object local = object()) {
if (!local) if (!local) {
local = global; local = global;
}
detail::ensure_builtins_in_globals(global); detail::ensure_builtins_in_globals(global);
int start = 0; int start = 0;
switch (mode) { switch (mode) {
case eval_expr: start = Py_eval_input; break; case eval_expr:
case eval_single_statement: start = Py_single_input; break; start = Py_eval_input;
case eval_statements: start = Py_file_input; break; break;
default: pybind11_fail("invalid evaluation mode"); case eval_single_statement:
start = Py_single_input;
break;
case eval_statements:
start = Py_file_input;
break;
default:
pybind11_fail("invalid evaluation mode");
} }
int closeFile = 1; int closeFile = 1;
std::string fname_str = (std::string) fname; std::string fname_str = (std::string) fname;
#if PY_VERSION_HEX >= 0x03040000 # if PY_VERSION_HEX >= 0x03040000
FILE *f = _Py_fopen_obj(fname.ptr(), "r"); FILE *f = _Py_fopen_obj(fname.ptr(), "r");
#elif PY_VERSION_HEX >= 0x03000000 # elif PY_VERSION_HEX >= 0x03000000
FILE *f = _Py_fopen(fname.ptr(), "r"); FILE *f = _Py_fopen(fname.ptr(), "r");
#else # else
/* No unicode support in open() :( */ /* No unicode support in open() :( */
auto fobj = reinterpret_steal<object>(PyFile_FromString( auto fobj = reinterpret_steal<object>(
const_cast<char *>(fname_str.c_str()), PyFile_FromString(const_cast<char *>(fname_str.c_str()), const_cast<char *>("r")));
const_cast<char*>("r")));
FILE *f = nullptr; FILE *f = nullptr;
if (fobj) if (fobj)
f = PyFile_AsFile(fobj.ptr()); f = PyFile_AsFile(fobj.ptr());
closeFile = 0; closeFile = 0;
#endif # endif
if (!f) { if (!f) {
PyErr_Clear(); PyErr_Clear();
pybind11_fail("File \"" + fname_str + "\" could not be opened!"); pybind11_fail("File \"" + fname_str + "\" could not be opened!");
} }
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) // In Python2, this should be encoded by getfilesystemencoding.
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), // We don't boher setting it since Python2 is past EOL anyway.
local.ptr()); // See PR#3233
(void) closeFile; # if PY_VERSION_HEX >= 0x03000000
#else if (!global.contains("__file__")) {
PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), global["__file__"] = std::move(fname);
local.ptr(), closeFile); }
#endif # endif
if (!result) # if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), local.ptr());
(void) closeFile;
# else
PyObject *result
= PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile);
# endif
if (!result) {
throw error_already_set(); throw error_already_set();
}
return reinterpret_steal<object>(result); return reinterpret_steal<object>(result);
} }
#endif #endif

View File

@ -10,6 +10,7 @@
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include <functional> #include <functional>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@ -19,18 +20,21 @@ template <typename Return, typename... Args>
struct type_caster<std::function<Return(Args...)>> { struct type_caster<std::function<Return(Args...)>> {
using type = std::function<Return(Args...)>; using type = std::function<Return(Args...)>;
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>; using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
using function_type = Return (*) (Args...); using function_type = Return (*)(Args...);
public: public:
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (src.is_none()) { if (src.is_none()) {
// Defer accepting None to other overloads (if we aren't in convert mode): // Defer accepting None to other overloads (if we aren't in convert mode):
if (!convert) return false; if (!convert) {
return false;
}
return true; return true;
} }
if (!isinstance<function>(src)) if (!isinstance<function>(src)) {
return false; return false;
}
auto func = reinterpret_borrow<function>(src); auto func = reinterpret_borrow<function>(src);
@ -43,10 +47,10 @@ public:
captured variables), in which case the roundtrip can be avoided. captured variables), in which case the roundtrip can be avoided.
*/ */
if (auto cfunc = func.cpp_function()) { if (auto cfunc = func.cpp_function()) {
auto cfunc_self = PyCFunction_GET_SELF(cfunc.ptr()); auto *cfunc_self = PyCFunction_GET_SELF(cfunc.ptr());
if (isinstance<capsule>(cfunc_self)) { if (isinstance<capsule>(cfunc_self)) {
auto c = reinterpret_borrow<capsule>(cfunc_self); auto c = reinterpret_borrow<capsule>(cfunc_self);
auto rec = (function_record *) c; auto *rec = (function_record *) c;
while (rec != nullptr) { while (rec != nullptr) {
if (rec->is_stateless if (rec->is_stateless
@ -69,7 +73,13 @@ public:
// ensure GIL is held during functor destruction // ensure GIL is held during functor destruction
struct func_handle { struct func_handle {
function f; function f;
func_handle(function &&f_) noexcept : f(std::move(f_)) {} #if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17))
// This triggers a syntax error under very special conditions (very weird indeed).
explicit
#endif
func_handle(function &&f_) noexcept
: f(std::move(f_)) {
}
func_handle(const func_handle &f_) { operator=(f_); } func_handle(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) { func_handle &operator=(const func_handle &f_) {
gil_scoped_acquire acq; gil_scoped_acquire acq;
@ -85,7 +95,7 @@ public:
// to emulate 'move initialization capture' in C++11 // to emulate 'move initialization capture' in C++11
struct func_wrapper { struct func_wrapper {
func_handle hfunc; func_handle hfunc;
func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {} explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
Return operator()(Args... args) const { Return operator()(Args... args) const {
gil_scoped_acquire acq; gil_scoped_acquire acq;
object retval(hfunc.f(std::forward<Args>(args)...)); object retval(hfunc.f(std::forward<Args>(args)...));
@ -100,17 +110,21 @@ public:
template <typename Func> template <typename Func>
static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) {
if (!f_) if (!f_) {
return none().inc_ref(); return none().inc_ref();
}
auto result = f_.template target<function_type>(); auto result = f_.template target<function_type>();
if (result) if (result) {
return cpp_function(*result, policy).release(); return cpp_function(*result, policy).release();
}
return cpp_function(std::forward<Func>(f_), policy).release(); return cpp_function(std::forward<Func>(f_), policy).release();
} }
PYBIND11_TYPE_CASTER(type, _x("Callable[[") + concat(make_caster<Args>::name...) + _x("], ") PYBIND11_TYPE_CASTER(type,
+ make_caster<retval_type>::name + _x("]")); const_name("Callable[[") + concat(make_caster<Args>::name...)
+ const_name("], ") + make_caster<retval_type>::name
+ const_name("]"));
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

View File

@ -14,7 +14,6 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
// forward declarations // forward declarations
@ -22,7 +21,6 @@ PyThreadState *get_thread_state_unchecked();
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
#if defined(WITH_THREAD) && !defined(PYPY_VERSION) #if defined(WITH_THREAD) && !defined(PYPY_VERSION)
/* The functions below essentially reproduce the PyGILState_* API using a RAII /* The functions below essentially reproduce the PyGILState_* API using a RAII
@ -50,7 +48,7 @@ PYBIND11_NAMESPACE_END(detail)
class gil_scoped_acquire { class gil_scoped_acquire {
public: public:
PYBIND11_NOINLINE gil_scoped_acquire() { PYBIND11_NOINLINE gil_scoped_acquire() {
auto const &internals = detail::get_internals(); auto &internals = detail::get_internals();
tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
if (!tstate) { if (!tstate) {
@ -64,10 +62,11 @@ public:
if (!tstate) { if (!tstate) {
tstate = PyThreadState_New(internals.istate); tstate = PyThreadState_New(internals.istate);
#if !defined(NDEBUG) # if !defined(NDEBUG)
if (!tstate) if (!tstate) {
pybind11_fail("scoped_acquire: could not create thread state!"); pybind11_fail("scoped_acquire: could not create thread state!");
#endif }
# endif
tstate->gilstate_counter = 0; tstate->gilstate_counter = 0;
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
} else { } else {
@ -81,26 +80,28 @@ public:
inc_ref(); inc_ref();
} }
void inc_ref() { void inc_ref() { ++tstate->gilstate_counter; }
++tstate->gilstate_counter;
}
PYBIND11_NOINLINE void dec_ref() { PYBIND11_NOINLINE void dec_ref() {
--tstate->gilstate_counter; --tstate->gilstate_counter;
#if !defined(NDEBUG) # if !defined(NDEBUG)
if (detail::get_thread_state_unchecked() != tstate) if (detail::get_thread_state_unchecked() != tstate) {
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
if (tstate->gilstate_counter < 0) }
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); if (tstate->gilstate_counter < 0) {
#endif pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
}
# endif
if (tstate->gilstate_counter == 0) { if (tstate->gilstate_counter == 0) {
#if !defined(NDEBUG) # if !defined(NDEBUG)
if (!release) if (!release) {
pybind11_fail("scoped_acquire::dec_ref(): internal error!"); pybind11_fail("scoped_acquire::dec_ref(): internal error!");
#endif }
# endif
PyThreadState_Clear(tstate); PyThreadState_Clear(tstate);
if (active) if (active) {
PyThreadState_DeleteCurrent(); PyThreadState_DeleteCurrent();
}
PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate);
release = false; release = false;
} }
@ -111,15 +112,15 @@ public:
/// could be shutting down when this is called, as thread deletion is not /// could be shutting down when this is called, as thread deletion is not
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
/// protect subsequent code. /// protect subsequent code.
PYBIND11_NOINLINE void disarm() { PYBIND11_NOINLINE void disarm() { active = false; }
active = false;
}
PYBIND11_NOINLINE ~gil_scoped_acquire() { PYBIND11_NOINLINE ~gil_scoped_acquire() {
dec_ref(); dec_ref();
if (release) if (release) {
PyEval_SaveThread(); PyEval_SaveThread();
}
} }
private: private:
PyThreadState *tstate = nullptr; PyThreadState *tstate = nullptr;
bool release = true; bool release = true;
@ -132,9 +133,12 @@ public:
// `get_internals()` must be called here unconditionally in order to initialize // `get_internals()` must be called here unconditionally in order to initialize
// `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
// initialization race could occur as multiple threads try `gil_scoped_acquire`. // initialization race could occur as multiple threads try `gil_scoped_acquire`.
const auto &internals = detail::get_internals(); auto &internals = detail::get_internals();
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
tstate = PyEval_SaveThread(); tstate = PyEval_SaveThread();
if (disassoc) { if (disassoc) {
// Python >= 3.7 can remove this, it's an int before 3.7
// NOLINTNEXTLINE(readability-qualified-auto)
auto key = internals.tstate; auto key = internals.tstate;
PYBIND11_TLS_DELETE_VALUE(key); PYBIND11_TLS_DELETE_VALUE(key);
} }
@ -145,21 +149,24 @@ public:
/// could be shutting down when this is called, as thread deletion is not /// could be shutting down when this is called, as thread deletion is not
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
/// protect subsequent code. /// protect subsequent code.
PYBIND11_NOINLINE void disarm() { PYBIND11_NOINLINE void disarm() { active = false; }
active = false;
}
~gil_scoped_release() { ~gil_scoped_release() {
if (!tstate) if (!tstate) {
return; return;
}
// `PyEval_RestoreThread()` should not be called if runtime is finalizing // `PyEval_RestoreThread()` should not be called if runtime is finalizing
if (active) if (active) {
PyEval_RestoreThread(tstate); PyEval_RestoreThread(tstate);
}
if (disassoc) { if (disassoc) {
// Python >= 3.7 can remove this, it's an int before 3.7
// NOLINTNEXTLINE(readability-qualified-auto)
auto key = detail::get_internals().tstate; auto key = detail::get_internals().tstate;
PYBIND11_TLS_REPLACE_VALUE(key, tstate); PYBIND11_TLS_REPLACE_VALUE(key, tstate);
} }
} }
private: private:
PyThreadState *tstate; PyThreadState *tstate;
bool disassoc; bool disassoc;
@ -168,6 +175,7 @@ private:
#elif defined(PYPY_VERSION) #elif defined(PYPY_VERSION)
class gil_scoped_acquire { class gil_scoped_acquire {
PyGILState_STATE state; PyGILState_STATE state;
public: public:
gil_scoped_acquire() { state = PyGILState_Ensure(); } gil_scoped_acquire() { state = PyGILState_Ensure(); }
~gil_scoped_acquire() { PyGILState_Release(state); } ~gil_scoped_acquire() { PyGILState_Release(state); }
@ -176,6 +184,7 @@ public:
class gil_scoped_release { class gil_scoped_release {
PyThreadState *state; PyThreadState *state;
public: public:
gil_scoped_release() { state = PyEval_SaveThread(); } gil_scoped_release() { state = PyEval_SaveThread(); }
~gil_scoped_release() { PyEval_RestoreThread(state); } ~gil_scoped_release() { PyEval_RestoreThread(state); }

View File

@ -58,36 +58,31 @@ private:
size_t utf8_remainder() const { size_t utf8_remainder() const {
const auto rbase = std::reverse_iterator<char *>(pbase()); const auto rbase = std::reverse_iterator<char *>(pbase());
const auto rpptr = std::reverse_iterator<char *>(pptr()); const auto rpptr = std::reverse_iterator<char *>(pptr());
auto is_ascii = [](char c) { auto is_ascii = [](char c) { return (static_cast<unsigned char>(c) & 0x80) == 0x00; };
return (static_cast<unsigned char>(c) & 0x80) == 0x00; auto is_leading = [](char c) { return (static_cast<unsigned char>(c) & 0xC0) == 0xC0; };
}; auto is_leading_2b = [](char c) { return static_cast<unsigned char>(c) <= 0xDF; };
auto is_leading = [](char c) { auto is_leading_3b = [](char c) { return static_cast<unsigned char>(c) <= 0xEF; };
return (static_cast<unsigned char>(c) & 0xC0) == 0xC0;
};
auto is_leading_2b = [](char c) {
return static_cast<unsigned char>(c) <= 0xDF;
};
auto is_leading_3b = [](char c) {
return static_cast<unsigned char>(c) <= 0xEF;
};
// If the last character is ASCII, there are no incomplete code points // If the last character is ASCII, there are no incomplete code points
if (is_ascii(*rpptr)) if (is_ascii(*rpptr)) {
return 0; return 0;
}
// Otherwise, work back from the end of the buffer and find the first // Otherwise, work back from the end of the buffer and find the first
// UTF-8 leading byte // UTF-8 leading byte
const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase; const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase;
const auto leading = std::find_if(rpptr, rpend, is_leading); const auto leading = std::find_if(rpptr, rpend, is_leading);
if (leading == rbase) if (leading == rbase) {
return 0; return 0;
const auto dist = static_cast<size_t>(leading - rpptr); }
size_t remainder = 0; const auto dist = static_cast<size_t>(leading - rpptr);
size_t remainder = 0;
if (dist == 0) if (dist == 0) {
remainder = 1; // 1-byte code point is impossible remainder = 1; // 1-byte code point is impossible
else if (dist == 1) } else if (dist == 1) {
remainder = is_leading_2b(*leading) ? 0 : dist + 1; remainder = is_leading_2b(*leading) ? 0 : dist + 1;
else if (dist == 2) } else if (dist == 2) {
remainder = is_leading_3b(*leading) ? 0 : dist + 1; remainder = is_leading_3b(*leading) ? 0 : dist + 1;
}
// else if (dist >= 3), at least 4 bytes before encountering an UTF-8 // else if (dist >= 3), at least 4 bytes before encountering an UTF-8
// leading byte, either no remainder or invalid UTF-8. // leading byte, either no remainder or invalid UTF-8.
// Invalid UTF-8 will cause an exception later when converting // Invalid UTF-8 will cause an exception later when converting
@ -100,7 +95,7 @@ private:
if (pbase() != pptr()) { // If buffer is not empty if (pbase() != pptr()) { // If buffer is not empty
gil_scoped_acquire tmp; gil_scoped_acquire tmp;
// This subtraction cannot be negative, so dropping the sign. // This subtraction cannot be negative, so dropping the sign.
auto size = static_cast<size_t>(pptr() - pbase()); auto size = static_cast<size_t>(pptr() - pbase());
size_t remainder = utf8_remainder(); size_t remainder = utf8_remainder();
if (size > remainder) { if (size > remainder) {
@ -110,36 +105,32 @@ private:
} }
// Copy the remainder at the end of the buffer to the beginning: // Copy the remainder at the end of the buffer to the beginning:
if (remainder > 0) if (remainder > 0) {
std::memmove(pbase(), pptr() - remainder, remainder); std::memmove(pbase(), pptr() - remainder, remainder);
}
setp(pbase(), epptr()); setp(pbase(), epptr());
pbump(static_cast<int>(remainder)); pbump(static_cast<int>(remainder));
} }
return 0; return 0;
} }
int sync() override { int sync() override { return _sync(); }
return _sync();
}
public: public:
pythonbuf(const object &pyostream, size_t buffer_size = 1024) explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024)
: buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")), : buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")),
pyflush(pyostream.attr("flush")) { pyflush(pyostream.attr("flush")) {
setp(d_buffer.get(), d_buffer.get() + buf_size - 1); setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
} }
pythonbuf(pythonbuf&&) = default; pythonbuf(pythonbuf &&) = default;
/// Sync before destroy /// Sync before destroy
~pythonbuf() override { ~pythonbuf() override { _sync(); }
_sync();
}
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/** \rst /** \rst
This a move-only guard that redirects output. This a move-only guard that redirects output.
@ -160,7 +151,8 @@ PYBIND11_NAMESPACE_END(detail)
.. code-block:: cpp .. code-block:: cpp
{ {
py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; py::scoped_ostream_redirect output{
std::cerr, py::module::import("sys").attr("stderr")};
std::cout << "Hello, World!"; std::cout << "Hello, World!";
} }
\endrst */ \endrst */
@ -171,15 +163,14 @@ protected:
detail::pythonbuf buffer; detail::pythonbuf buffer;
public: public:
scoped_ostream_redirect(std::ostream &costream = std::cout, explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
const object &pyostream = module_::import("sys").attr("stdout")) const object &pyostream
= module_::import("sys").attr("stdout"))
: costream(costream), buffer(pyostream) { : costream(costream), buffer(pyostream) {
old = costream.rdbuf(&buffer); old = costream.rdbuf(&buffer);
} }
~scoped_ostream_redirect() { ~scoped_ostream_redirect() { costream.rdbuf(old); }
costream.rdbuf(old);
}
scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; scoped_ostream_redirect(const scoped_ostream_redirect &) = delete;
scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; scoped_ostream_redirect(scoped_ostream_redirect &&other) = default;
@ -187,7 +178,6 @@ public:
scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete;
}; };
/** \rst /** \rst
Like `scoped_ostream_redirect`, but redirects cerr by default. This class Like `scoped_ostream_redirect`, but redirects cerr by default. This class
is provided primary to make ``py::call_guard`` easier to make. is provided primary to make ``py::call_guard`` easier to make.
@ -201,12 +191,12 @@ public:
\endrst */ \endrst */
class scoped_estream_redirect : public scoped_ostream_redirect { class scoped_estream_redirect : public scoped_ostream_redirect {
public: public:
scoped_estream_redirect(std::ostream &costream = std::cerr, explicit scoped_estream_redirect(std::ostream &costream = std::cerr,
const object &pyostream = module_::import("sys").attr("stderr")) const object &pyostream
= module_::import("sys").attr("stderr"))
: scoped_ostream_redirect(costream, pyostream) {} : scoped_ostream_redirect(costream, pyostream) {}
}; };
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
// Class to redirect output as a context manager. C++ backend. // Class to redirect output as a context manager. C++ backend.
@ -217,14 +207,16 @@ class OstreamRedirect {
std::unique_ptr<scoped_estream_redirect> redirect_stderr; std::unique_ptr<scoped_estream_redirect> redirect_stderr;
public: public:
OstreamRedirect(bool do_stdout = true, bool do_stderr = true) explicit OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
: do_stdout_(do_stdout), do_stderr_(do_stderr) {} : do_stdout_(do_stdout), do_stderr_(do_stderr) {}
void enter() { void enter() {
if (do_stdout_) if (do_stdout_) {
redirect_stdout.reset(new scoped_ostream_redirect()); redirect_stdout.reset(new scoped_ostream_redirect());
if (do_stderr_) }
if (do_stderr_) {
redirect_stderr.reset(new scoped_estream_redirect()); redirect_stderr.reset(new scoped_estream_redirect());
}
} }
void exit() { void exit() {

File diff suppressed because it is too large Load Diff

View File

@ -11,24 +11,55 @@
#include "pybind11.h" #include "pybind11.h"
#if defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type()))
#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
/// Enumeration with all supported operator types /// Enumeration with all supported operator types
enum op_id : int { enum op_id : int {
op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, op_add,
op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, op_sub,
op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, op_mul,
op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, op_div,
op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, op_mod,
op_repr, op_truediv, op_itruediv, op_hash op_divmod,
op_pow,
op_lshift,
op_rshift,
op_and,
op_xor,
op_or,
op_neg,
op_pos,
op_abs,
op_invert,
op_int,
op_long,
op_float,
op_str,
op_cmp,
op_gt,
op_ge,
op_lt,
op_le,
op_eq,
op_ne,
op_iadd,
op_isub,
op_imul,
op_idiv,
op_imod,
op_ilshift,
op_irshift,
op_iand,
op_ixor,
op_ior,
op_complex,
op_bool,
op_nonzero,
op_repr,
op_truediv,
op_itruediv,
op_hash
}; };
enum op_type : int { enum op_type : int {
@ -37,125 +68,145 @@ enum op_type : int {
op_u /* unary operator */ op_u /* unary operator */
}; };
struct self_t { }; struct self_t {};
static const self_t self = self_t(); static const self_t self = self_t();
/// Type for an unused type slot /// Type for an unused type slot
struct undefined_t { }; struct undefined_t {};
/// Don't warn about an unused variable /// Don't warn about an unused variable
inline self_t __self() { return self; } inline self_t __self() { return self; }
/// base template of operator implementations /// base template of operator implementations
template <op_id, op_type, typename B, typename L, typename R> struct op_impl { }; template <op_id, op_type, typename B, typename L, typename R>
struct op_impl {};
/// Operator implementation generator /// Operator implementation generator
template <op_id id, op_type ot, typename L, typename R> struct op_ { template <op_id id, op_type ot, typename L, typename R>
template <typename Class, typename... Extra> void execute(Class &cl, const Extra&... extra) const { struct op_ {
template <typename Class, typename... Extra>
void execute(Class &cl, const Extra &...extra) const {
using Base = typename Class::type; using Base = typename Class::type;
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>; using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>;
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>; using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
using op = op_impl<id, ot, Base, L_type, R_type>; using op = op_impl<id, ot, Base, L_type, R_type>;
cl.def(op::name(), &op::execute, is_operator(), extra...); cl.def(op::name(), &op::execute, is_operator(), extra...);
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (id == op_truediv || id == op_itruediv) if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv)
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", || PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
&op::execute, is_operator(), extra...); cl.def(id == op_itruediv ? "__idiv__"
#endif : ot == op_l ? "__div__"
: "__rdiv__",
&op::execute,
is_operator(),
extra...);
#endif
} }
template <typename Class, typename... Extra> void execute_cast(Class &cl, const Extra&... extra) const { template <typename Class, typename... Extra>
void execute_cast(Class &cl, const Extra &...extra) const {
using Base = typename Class::type; using Base = typename Class::type;
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>; using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>;
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>; using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
using op = op_impl<id, ot, Base, L_type, R_type>; using op = op_impl<id, ot, Base, L_type, R_type>;
cl.def(op::name(), &op::execute_cast, is_operator(), extra...); cl.def(op::name(), &op::execute_cast, is_operator(), extra...);
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (id == op_truediv || id == op_itruediv) if (id == op_truediv || id == op_itruediv)
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", cl.def(id == op_itruediv ? "__idiv__"
&op::execute, is_operator(), extra...); : ot == op_l ? "__div__"
#endif : "__rdiv__",
&op::execute,
is_operator(),
extra...);
#endif
} }
}; };
#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ #define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \ template <typename B, typename L, typename R> \
static char const* name() { return "__" #id "__"; } \ struct op_impl<op_##id, op_l, B, L, R> { \
static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ static char const *name() { return "__" #id "__"; } \
static B execute_cast(const L &l, const R &r) { return B(expr); } \ static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \
}; \ static B execute_cast(const L &l, const R &r) { return B(expr); } \
template <typename B, typename L, typename R> struct op_impl<op_##id, op_r, B, L, R> { \ }; \
static char const* name() { return "__" #rid "__"; } \ template <typename B, typename L, typename R> \
static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ struct op_impl<op_##id, op_r, B, L, R> { \
static B execute_cast(const R &r, const L &l) { return B(expr); } \ static char const *name() { return "__" #rid "__"; } \
}; \ static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \
inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) { \ static B execute_cast(const R &r, const L &l) { return B(expr); } \
return op_<op_##id, op_l, self_t, self_t>(); \ }; \
} \ inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) { \
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \ return op_<op_##id, op_l, self_t, self_t>(); \
return op_<op_##id, op_l, self_t, T>(); \ } \
} \ template <typename T> \
template <typename T> op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) { \ op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
return op_<op_##id, op_r, T, self_t>(); \ return op_<op_##id, op_l, self_t, T>(); \
} } \
template <typename T> \
op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) { \
return op_<op_##id, op_r, T, self_t>(); \
}
#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ #define PYBIND11_INPLACE_OPERATOR(id, op, expr) \
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \ template <typename B, typename L, typename R> \
static char const* name() { return "__" #id "__"; } \ struct op_impl<op_##id, op_l, B, L, R> { \
static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ static char const *name() { return "__" #id "__"; } \
static B execute_cast(L &l, const R &r) { return B(expr); } \ static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \
}; \ static B execute_cast(L &l, const R &r) { return B(expr); } \
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \ }; \
return op_<op_##id, op_l, self_t, T>(); \ template <typename T> \
} op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
return op_<op_##id, op_l, self_t, T>(); \
}
#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ #define PYBIND11_UNARY_OPERATOR(id, op, expr) \
template <typename B, typename L> struct op_impl<op_##id, op_u, B, L, undefined_t> { \ template <typename B, typename L> \
static char const* name() { return "__" #id "__"; } \ struct op_impl<op_##id, op_u, B, L, undefined_t> { \
static auto execute(const L &l) -> decltype(expr) { return expr; } \ static char const *name() { return "__" #id "__"; } \
static B execute_cast(const L &l) { return B(expr); } \ static auto execute(const L &l) -> decltype(expr) { return expr; } \
}; \ static B execute_cast(const L &l) { return B(expr); } \
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \ }; \
return op_<op_##id, op_u, self_t, undefined_t>(); \ inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \
} return op_<op_##id, op_u, self_t, undefined_t>(); \
}
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r)
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l *r)
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) PYBIND11_BINARY_OPERATOR(and, rand, operator&, l &r)
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) // PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r)
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r)
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
PYBIND11_UNARY_OPERATOR(neg, operator-, -l) PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
PYBIND11_UNARY_OPERATOR(pos, operator+, +l) PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
// WARNING: This usage of `abs` should only be done for existing STL overloads. // WARNING: This usage of `abs` should only be done for existing STL overloads.
// Adding overloads directly in to the `std::` namespace is advised against: // Adding overloads directly in to the `std::` namespace is advised against:
// https://en.cppreference.com/w/cpp/language/extending_std // https://en.cppreference.com/w/cpp/language/extending_std
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l)) PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
PYBIND11_UNARY_OPERATOR(int, int_, (int) l) PYBIND11_UNARY_OPERATOR(int, int_, (int) l)
PYBIND11_UNARY_OPERATOR(float, float_, (double) l) PYBIND11_UNARY_OPERATOR(float, float_, (double) l)
#undef PYBIND11_BINARY_OPERATOR #undef PYBIND11_BINARY_OPERATOR
#undef PYBIND11_INPLACE_OPERATOR #undef PYBIND11_INPLACE_OPERATOR
@ -167,7 +218,3 @@ using detail::self;
using detail::hash; using detail::hash;
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif

View File

@ -15,43 +15,54 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
class options { class options {
public: public:
// Default RAII constructor, which leaves settings as they currently are. // Default RAII constructor, which leaves settings as they currently are.
options() : previous_state(global_state()) {} options() : previous_state(global_state()) {}
// Class is non-copyable. // Class is non-copyable.
options(const options&) = delete; options(const options &) = delete;
options& operator=(const options&) = delete; options &operator=(const options &) = delete;
// Destructor, which restores settings that were in effect before. // Destructor, which restores settings that were in effect before.
~options() { ~options() { global_state() = previous_state; }
global_state() = previous_state;
}
// Setter methods (affect the global state): // Setter methods (affect the global state):
options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } options &disable_user_defined_docstrings() & {
global_state().show_user_defined_docstrings = false;
return *this;
}
options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } options &enable_user_defined_docstrings() & {
global_state().show_user_defined_docstrings = true;
return *this;
}
options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } options &disable_function_signatures() & {
global_state().show_function_signatures = false;
return *this;
}
options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } options &enable_function_signatures() & {
global_state().show_function_signatures = true;
return *this;
}
// Getter methods (return the global state): // Getter methods (return the global state):
static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } static bool show_user_defined_docstrings() {
return global_state().show_user_defined_docstrings;
}
static bool show_function_signatures() { return global_state().show_function_signatures; } static bool show_function_signatures() { return global_state().show_function_signatures; }
// This type is not meant to be allocated on the heap. // This type is not meant to be allocated on the heap.
void* operator new(size_t) = delete; void *operator new(size_t) = delete;
private: private:
struct state { struct state {
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. bool show_function_signatures = true; //< Include auto-generated function signatures
// in docstrings.
}; };
static state &global_state() { static state &global_state() {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,42 +10,26 @@
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include <set> #include "detail/common.h"
#include <unordered_set>
#include <map> #include <deque>
#include <unordered_map>
#include <iostream> #include <iostream>
#include <list> #include <list>
#include <deque> #include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <valarray> #include <valarray>
#if defined(_MSC_VER) // See `detail/common.h` for implementation of these guards.
#pragma warning(push) #if defined(PYBIND11_HAS_OPTIONAL)
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant # include <optional>
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
# include <experimental/optional>
#endif #endif
#ifdef __has_include #if defined(PYBIND11_HAS_VARIANT)
// std::optional (but including it in c++14 mode isn't allowed)
# if defined(PYBIND11_CPP17) && __has_include(<optional>)
# include <optional>
# define PYBIND11_HAS_OPTIONAL 1
# endif
// std::experimental::optional (but not allowed in c++11 mode)
# if defined(PYBIND11_CPP14) && (__has_include(<experimental/optional>) && \
!__has_include(<optional>))
# include <experimental/optional>
# define PYBIND11_HAS_EXP_OPTIONAL 1
# endif
// std::variant
# if defined(PYBIND11_CPP17) && __has_include(<variant>)
# include <variant> # include <variant>
# define PYBIND11_HAS_VARIANT 1
# endif
#elif defined(_MSC_VER) && defined(PYBIND11_CPP17)
# include <optional>
# include <variant>
# define PYBIND11_HAS_OPTIONAL 1
# define PYBIND11_HAS_VARIANT 1
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@ -54,8 +38,9 @@ PYBIND11_NAMESPACE_BEGIN(detail)
/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for /// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for
/// forwarding a container element). Typically used indirect via forwarded_type(), below. /// forwarding a container element). Typically used indirect via forwarded_type(), below.
template <typename T, typename U> template <typename T, typename U>
using forwarded_type = conditional_t< using forwarded_type = conditional_t<std::is_lvalue_reference<T>::value,
std::is_lvalue_reference<T>::value, remove_reference_t<U> &, remove_reference_t<U> &&>; remove_reference_t<U> &,
remove_reference_t<U> &&>;
/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically /// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically
/// used for forwarding a container's elements. /// used for forwarding a container's elements.
@ -64,19 +49,22 @@ forwarded_type<T, U> forward_like(U &&u) {
return std::forward<detail::forwarded_type<T, U>>(std::forward<U>(u)); return std::forward<detail::forwarded_type<T, U>>(std::forward<U>(u));
} }
template <typename Type, typename Key> struct set_caster { template <typename Type, typename Key>
struct set_caster {
using type = Type; using type = Type;
using key_conv = make_caster<Key>; using key_conv = make_caster<Key>;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!isinstance<pybind11::set>(src)) if (!isinstance<pybind11::set>(src)) {
return false; return false;
}
auto s = reinterpret_borrow<pybind11::set>(src); auto s = reinterpret_borrow<pybind11::set>(src);
value.clear(); value.clear();
for (auto entry : s) { for (auto entry : s) {
key_conv conv; key_conv conv;
if (!conv.load(entry, convert)) if (!conv.load(entry, convert)) {
return false; return false;
}
value.insert(cast_op<Key &&>(std::move(conv))); value.insert(cast_op<Key &&>(std::move(conv)));
} }
return true; return true;
@ -84,35 +72,40 @@ template <typename Type, typename Key> struct set_caster {
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
if (!std::is_lvalue_reference<T>::value) if (!std::is_lvalue_reference<T>::value) {
policy = return_value_policy_override<Key>::policy(policy); policy = return_value_policy_override<Key>::policy(policy);
}
pybind11::set s; pybind11::set s;
for (auto &&value : src) { for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(key_conv::cast(forward_like<T>(value), policy, parent)); auto value_ = reinterpret_steal<object>(
if (!value_ || !s.add(value_)) key_conv::cast(forward_like<T>(value), policy, parent));
if (!value_ || !s.add(value_)) {
return handle(); return handle();
}
} }
return s.release(); return s.release();
} }
PYBIND11_TYPE_CASTER(type, _x("Set[") + key_conv::name + _x("]")); PYBIND11_TYPE_CASTER(type, const_name("Set[") + key_conv::name + const_name("]"));
}; };
template <typename Type, typename Key, typename Value> struct map_caster { template <typename Type, typename Key, typename Value>
using key_conv = make_caster<Key>; struct map_caster {
using key_conv = make_caster<Key>;
using value_conv = make_caster<Value>; using value_conv = make_caster<Value>;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!isinstance<dict>(src)) if (!isinstance<dict>(src)) {
return false; return false;
}
auto d = reinterpret_borrow<dict>(src); auto d = reinterpret_borrow<dict>(src);
value.clear(); value.clear();
for (auto it : d) { for (auto it : d) {
key_conv kconv; key_conv kconv;
value_conv vconv; value_conv vconv;
if (!kconv.load(it.first.ptr(), convert) || if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) {
!vconv.load(it.second.ptr(), convert))
return false; return false;
}
value.emplace(cast_op<Key &&>(std::move(kconv)), cast_op<Value &&>(std::move(vconv))); value.emplace(cast_op<Key &&>(std::move(kconv)), cast_op<Value &&>(std::move(vconv)));
} }
return true; return true;
@ -128,31 +121,39 @@ template <typename Type, typename Key, typename Value> struct map_caster {
policy_value = return_value_policy_override<Value>::policy(policy_value); policy_value = return_value_policy_override<Value>::policy(policy_value);
} }
for (auto &&kv : src) { for (auto &&kv : src) {
auto key = reinterpret_steal<object>(key_conv::cast(forward_like<T>(kv.first), policy_key, parent)); auto key = reinterpret_steal<object>(
auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy_value, parent)); key_conv::cast(forward_like<T>(kv.first), policy_key, parent));
if (!key || !value) auto value = reinterpret_steal<object>(
value_conv::cast(forward_like<T>(kv.second), policy_value, parent));
if (!key || !value) {
return handle(); return handle();
}
d[key] = value; d[key] = value;
} }
return d.release(); return d.release();
} }
PYBIND11_TYPE_CASTER(Type, _x("Dict[") + key_conv::name + _x(", ") + value_conv::name + _x("]")); PYBIND11_TYPE_CASTER(Type,
const_name("Dict[") + key_conv::name + const_name(", ") + value_conv::name
+ const_name("]"));
}; };
template <typename Type, typename Value> struct list_caster { template <typename Type, typename Value>
struct list_caster {
using value_conv = make_caster<Value>; using value_conv = make_caster<Value>;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src)) if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src)) {
return false; return false;
}
auto s = reinterpret_borrow<sequence>(src); auto s = reinterpret_borrow<sequence>(src);
value.clear(); value.clear();
reserve_maybe(s, &value); reserve_maybe(s, &value);
for (auto it : s) { for (auto it : s) {
value_conv conv; value_conv conv;
if (!conv.load(it, convert)) if (!conv.load(it, convert)) {
return false; return false;
}
value.push_back(cast_op<Value &&>(std::move(conv))); value.push_back(cast_op<Value &&>(std::move(conv)));
} }
return true; return true;
@ -160,7 +161,7 @@ template <typename Type, typename Value> struct list_caster {
private: private:
template < template <
typename T = Type, typename T = Type,
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0> enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
void reserve_maybe(const sequence &s, Type *) { void reserve_maybe(const sequence &s, Type *) {
value.reserve(s.size()); value.reserve(s.size());
@ -170,39 +171,44 @@ private:
public: public:
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
if (!std::is_lvalue_reference<T>::value) if (!std::is_lvalue_reference<T>::value) {
policy = return_value_policy_override<Value>::policy(policy); policy = return_value_policy_override<Value>::policy(policy);
}
list l(src.size()); list l(src.size());
size_t index = 0; ssize_t index = 0;
for (auto &&value : src) { for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent)); auto value_ = reinterpret_steal<object>(
if (!value_) value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_) {
return handle(); return handle();
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference }
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
} }
return l.release(); return l.release();
} }
PYBIND11_TYPE_CASTER(Type, _x("List[") + value_conv::name + _x("]")); PYBIND11_TYPE_CASTER(Type, const_name("List[") + value_conv::name + const_name("]"));
}; };
template <typename Type, typename Alloc> struct type_caster<std::vector<Type, Alloc>> template <typename Type, typename Alloc>
: list_caster<std::vector<Type, Alloc>, Type> { }; struct type_caster<std::vector<Type, Alloc>> : list_caster<std::vector<Type, Alloc>, Type> {};
template <typename Type, typename Alloc> struct type_caster<std::deque<Type, Alloc>> template <typename Type, typename Alloc>
: list_caster<std::deque<Type, Alloc>, Type> { }; struct type_caster<std::deque<Type, Alloc>> : list_caster<std::deque<Type, Alloc>, Type> {};
template <typename Type, typename Alloc> struct type_caster<std::list<Type, Alloc>> template <typename Type, typename Alloc>
: list_caster<std::list<Type, Alloc>, Type> { }; struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {};
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0> struct array_caster { template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0>
struct array_caster {
using value_conv = make_caster<Value>; using value_conv = make_caster<Value>;
private: private:
template <bool R = Resizable> template <bool R = Resizable>
bool require_size(enable_if_t<R, size_t> size) { bool require_size(enable_if_t<R, size_t> size) {
if (value.size() != size) if (value.size() != size) {
value.resize(size); value.resize(size);
}
return true; return true;
} }
template <bool R = Resizable> template <bool R = Resizable>
@ -212,16 +218,19 @@ private:
public: public:
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!isinstance<sequence>(src)) if (!isinstance<sequence>(src)) {
return false; return false;
}
auto l = reinterpret_borrow<sequence>(src); auto l = reinterpret_borrow<sequence>(src);
if (!require_size(l.size())) if (!require_size(l.size())) {
return false; return false;
}
size_t ctr = 0; size_t ctr = 0;
for (auto it : l) { for (auto it : l) {
value_conv conv; value_conv conv;
if (!conv.load(it, convert)) if (!conv.load(it, convert)) {
return false; return false;
}
value[ctr++] = cast_op<Value &&>(std::move(conv)); value[ctr++] = cast_op<Value &&>(std::move(conv));
} }
return true; return true;
@ -230,49 +239,63 @@ public:
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size()); list l(src.size());
size_t index = 0; ssize_t index = 0;
for (auto &&value : src) { for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent)); auto value_ = reinterpret_steal<object>(
if (!value_) value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_) {
return handle(); return handle();
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference }
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
} }
return l.release(); return l.release();
} }
PYBIND11_TYPE_CASTER(ArrayType, _x("List[") + value_conv::name + _x<Resizable>(_x(""), _x("[") + _x<Size>() + _x("]")) + _x("]")); PYBIND11_TYPE_CASTER(ArrayType,
const_name("List[") + value_conv::name
+ const_name<Resizable>(const_name(""),
const_name("[") + const_name<Size>()
+ const_name("]"))
+ const_name("]"));
}; };
template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>> template <typename Type, size_t Size>
: array_caster<std::array<Type, Size>, Type, false, Size> { }; struct type_caster<std::array<Type, Size>>
: array_caster<std::array<Type, Size>, Type, false, Size> {};
template <typename Type> struct type_caster<std::valarray<Type>> template <typename Type>
: array_caster<std::valarray<Type>, Type, true> { }; struct type_caster<std::valarray<Type>> : array_caster<std::valarray<Type>, Type, true> {};
template <typename Key, typename Compare, typename Alloc> struct type_caster<std::set<Key, Compare, Alloc>> template <typename Key, typename Compare, typename Alloc>
: set_caster<std::set<Key, Compare, Alloc>, Key> { }; struct type_caster<std::set<Key, Compare, Alloc>>
: set_caster<std::set<Key, Compare, Alloc>, Key> {};
template <typename Key, typename Hash, typename Equal, typename Alloc> struct type_caster<std::unordered_set<Key, Hash, Equal, Alloc>> template <typename Key, typename Hash, typename Equal, typename Alloc>
: set_caster<std::unordered_set<Key, Hash, Equal, Alloc>, Key> { }; struct type_caster<std::unordered_set<Key, Hash, Equal, Alloc>>
: set_caster<std::unordered_set<Key, Hash, Equal, Alloc>, Key> {};
template <typename Key, typename Value, typename Compare, typename Alloc> struct type_caster<std::map<Key, Value, Compare, Alloc>> template <typename Key, typename Value, typename Compare, typename Alloc>
: map_caster<std::map<Key, Value, Compare, Alloc>, Key, Value> { }; struct type_caster<std::map<Key, Value, Compare, Alloc>>
: map_caster<std::map<Key, Value, Compare, Alloc>, Key, Value> {};
template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc> struct type_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>> template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc>
: map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> { }; struct type_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>>
: map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> {};
// This type caster is intended to be used for std::optional and std::experimental::optional // This type caster is intended to be used for std::optional and std::experimental::optional
template<typename T> struct optional_caster { template <typename Type, typename Value = typename Type::value_type>
using value_conv = make_caster<typename T::value_type>; struct optional_caster {
using value_conv = make_caster<Value>;
template <typename T_> template <typename T>
static handle cast(T_ &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
if (!src) if (!src) {
return none().inc_ref(); return none().inc_ref();
if (!std::is_lvalue_reference<T>::value) {
policy = return_value_policy_override<T>::policy(policy);
} }
return value_conv::cast(*std::forward<T_>(src), policy, parent); if (!std::is_lvalue_reference<T>::value) {
policy = return_value_policy_override<Value>::policy(policy);
}
return value_conv::cast(*std::forward<T>(src), policy, parent);
} }
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
@ -280,32 +303,35 @@ template<typename T> struct optional_caster {
return false; return false;
} }
if (src.is_none()) { if (src.is_none()) {
return true; // default-constructed value is already empty return true; // default-constructed value is already empty
} }
value_conv inner_caster; value_conv inner_caster;
if (!inner_caster.load(src, convert)) if (!inner_caster.load(src, convert)) {
return false; return false;
}
value.emplace(cast_op<typename T::value_type &&>(std::move(inner_caster))); value.emplace(cast_op<Value &&>(std::move(inner_caster)));
return true; return true;
} }
PYBIND11_TYPE_CASTER(T, _x("Optional[") + value_conv::name + _x("]")); PYBIND11_TYPE_CASTER(Type, const_name("Optional[") + value_conv::name + const_name("]"));
}; };
#if defined(PYBIND11_HAS_OPTIONAL) #if defined(PYBIND11_HAS_OPTIONAL)
template<typename T> struct type_caster<std::optional<T>> template <typename T>
: public optional_caster<std::optional<T>> {}; struct type_caster<std::optional<T>> : public optional_caster<std::optional<T>> {};
template<> struct type_caster<std::nullopt_t> template <>
: public void_caster<std::nullopt_t> {}; struct type_caster<std::nullopt_t> : public void_caster<std::nullopt_t> {};
#endif #endif
#if defined(PYBIND11_HAS_EXP_OPTIONAL) #if defined(PYBIND11_HAS_EXP_OPTIONAL)
template<typename T> struct type_caster<std::experimental::optional<T>> template <typename T>
struct type_caster<std::experimental::optional<T>>
: public optional_caster<std::experimental::optional<T>> {}; : public optional_caster<std::experimental::optional<T>> {};
template<> struct type_caster<std::experimental::nullopt_t> template <>
struct type_caster<std::experimental::nullopt_t>
: public void_caster<std::experimental::nullopt_t> {}; : public void_caster<std::experimental::nullopt_t> {};
#endif #endif
@ -326,7 +352,7 @@ struct variant_caster_visitor {
/// `namespace::variant` types which provide a `namespace::visit()` function are handled here /// `namespace::variant` types which provide a `namespace::visit()` function are handled here
/// automatically using argument-dependent lookup. Users can provide specializations for other /// automatically using argument-dependent lookup. Users can provide specializations for other
/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. /// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`.
template <template<typename...> class Variant> template <template <typename...> class Variant>
struct visit_helper { struct visit_helper {
template <typename... Args> template <typename... Args>
static auto call(Args &&...args) -> decltype(visit(std::forward<Args>(args)...)) { static auto call(Args &&...args) -> decltype(visit(std::forward<Args>(args)...)) {
@ -335,9 +361,10 @@ struct visit_helper {
}; };
/// Generic variant caster /// Generic variant caster
template <typename Variant> struct variant_caster; template <typename Variant>
struct variant_caster;
template <template<typename...> class V, typename... Ts> template <template <typename...> class V, typename... Ts>
struct variant_caster<V<Ts...>> { struct variant_caster<V<Ts...>> {
static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative.");
@ -358,8 +385,9 @@ struct variant_caster<V<Ts...>> {
// E.g. `py::int_(1).cast<variant<double, int>>()` needs to fill the `int` // E.g. `py::int_(1).cast<variant<double, int>>()` needs to fill the `int`
// slot of the variant. Without two-pass loading `double` would be filled // slot of the variant. Without two-pass loading `double` would be filled
// because it appears first and a conversion is possible. // because it appears first and a conversion is possible.
if (convert && load_alternative(src, false, type_list<Ts...>{})) if (convert && load_alternative(src, false, type_list<Ts...>{})) {
return true; return true;
}
return load_alternative(src, convert, type_list<Ts...>{}); return load_alternative(src, convert, type_list<Ts...>{});
} }
@ -370,12 +398,14 @@ struct variant_caster<V<Ts...>> {
} }
using Type = V<Ts...>; using Type = V<Ts...>;
PYBIND11_TYPE_CASTER(Type, _x("Union[") + detail::concat(make_caster<Ts>::name...) + _x("]")); PYBIND11_TYPE_CASTER(Type,
const_name("Union[") + detail::concat(make_caster<Ts>::name...)
+ const_name("]"));
}; };
#if defined(PYBIND11_HAS_VARIANT) #if defined(PYBIND11_HAS_VARIANT)
template <typename... Ts> template <typename... Ts>
struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> { }; struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> {};
#endif #endif
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
@ -390,7 +420,3 @@ inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
} }
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
#pragma warning(pop)
#endif

View File

@ -4,21 +4,20 @@
#pragma once #pragma once
#include "../cast.h"
#include "../pybind11.h" #include "../pybind11.h"
#include "../pytypes.h"
#include "../detail/common.h" #include "../detail/common.h"
#include "../detail/descr.h" #include "../detail/descr.h"
#include "../cast.h"
#include "../pytypes.h"
#include <string> #include <string>
#ifdef __has_include #ifdef __has_include
# if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \ # if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \
PY_VERSION_HEX >= 0x03060000 PY_VERSION_HEX >= 0x03060000
# include <filesystem> # include <filesystem>
# define PYBIND11_HAS_FILESYSTEM 1 # define PYBIND11_HAS_FILESYSTEM 1
# endif # endif
#endif #endif
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL) #if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
@ -30,28 +29,29 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if defined(PYBIND11_HAS_FILESYSTEM) #if defined(PYBIND11_HAS_FILESYSTEM)
template<typename T> struct path_caster { template <typename T>
struct path_caster {
private: private:
static PyObject* unicode_from_fs_native(const std::string& w) { static PyObject *unicode_from_fs_native(const std::string &w) {
#if !defined(PYPY_VERSION) # if !defined(PYPY_VERSION)
return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size())); return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size()));
#else # else
// PyPy mistakenly declares the first parameter as non-const. // PyPy mistakenly declares the first parameter as non-const.
return PyUnicode_DecodeFSDefaultAndSize( return PyUnicode_DecodeFSDefaultAndSize(const_cast<char *>(w.c_str()), ssize_t(w.size()));
const_cast<char*>(w.c_str()), ssize_t(w.size())); # endif
#endif
} }
static PyObject* unicode_from_fs_native(const std::wstring& w) { static PyObject *unicode_from_fs_native(const std::wstring &w) {
return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size())); return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size()));
} }
public: public:
static handle cast(const T& path, return_value_policy, handle) { static handle cast(const T &path, return_value_policy, handle) {
if (auto py_str = unicode_from_fs_native(path.native())) { if (auto py_str = unicode_from_fs_native(path.native())) {
return module_::import("pathlib").attr("Path")(reinterpret_steal<object>(py_str)) return module_::import("pathlib")
.release(); .attr("Path")(reinterpret_steal<object>(py_str))
.release();
} }
return nullptr; return nullptr;
} }
@ -60,15 +60,15 @@ public:
// PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of // PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of
// calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy // calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy
// issue #3168) so we do it ourselves instead. // issue #3168) so we do it ourselves instead.
PyObject* buf = PyOS_FSPath(handle.ptr()); PyObject *buf = PyOS_FSPath(handle.ptr());
if (!buf) { if (!buf) {
PyErr_Clear(); PyErr_Clear();
return false; return false;
} }
PyObject* native = nullptr; PyObject *native = nullptr;
if constexpr (std::is_same_v<typename T::value_type, char>) { if constexpr (std::is_same_v<typename T::value_type, char>) {
if (PyUnicode_FSConverter(buf, &native) != 0) { if (PyUnicode_FSConverter(buf, &native) != 0) {
if (auto c_str = PyBytes_AsString(native)) { if (auto *c_str = PyBytes_AsString(native)) {
// AsString returns a pointer to the internal buffer, which // AsString returns a pointer to the internal buffer, which
// must not be free'd. // must not be free'd.
value = c_str; value = c_str;
@ -76,9 +76,9 @@ public:
} }
} else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) { } else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) {
if (PyUnicode_FSDecoder(buf, &native) != 0) { if (PyUnicode_FSDecoder(buf, &native) != 0) {
if (auto c_str = PyUnicode_AsWideCharString(native, nullptr)) { if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) {
// AsWideCharString returns a new string that must be free'd. // AsWideCharString returns a new string that must be free'd.
value = c_str; // Copies the string. value = c_str; // Copies the string.
PyMem_Free(c_str); PyMem_Free(c_str);
} }
} }
@ -92,11 +92,11 @@ public:
return true; return true;
} }
PYBIND11_TYPE_CASTER(T, _x("os.PathLike")); PYBIND11_TYPE_CASTER(T, const_name("os.PathLike"));
}; };
template<> struct type_caster<std::filesystem::path> template <>
: public path_caster<std::filesystem::path> {}; struct type_caster<std::filesystem::path> : public path_caster<std::filesystem::path> {};
#endif // PYBIND11_HAS_FILESYSTEM #endif // PYBIND11_HAS_FILESYSTEM
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

View File

@ -19,137 +19,148 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
/* SFINAE helper class used by 'is_comparable */ /* SFINAE helper class used by 'is_comparable */
template <typename T> struct container_traits { template <typename T>
template <typename T2> static std::true_type test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>())*); struct container_traits {
template <typename T2> static std::false_type test_comparable(...); template <typename T2>
template <typename T2> static std::true_type test_value(typename T2::value_type *); static std::true_type
template <typename T2> static std::false_type test_value(...); test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>()) *);
template <typename T2> static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); template <typename T2>
template <typename T2> static std::false_type test_pair(...); static std::false_type test_comparable(...);
template <typename T2>
static std::true_type test_value(typename T2::value_type *);
template <typename T2>
static std::false_type test_value(...);
template <typename T2>
static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *);
template <typename T2>
static std::false_type test_pair(...);
static constexpr const bool is_comparable = std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value; static constexpr const bool is_comparable
static constexpr const bool is_pair = std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value; = std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value;
static constexpr const bool is_vector = std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value; static constexpr const bool is_pair
= std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value;
static constexpr const bool is_vector
= std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value;
static constexpr const bool is_element = !is_pair && !is_vector; static constexpr const bool is_element = !is_pair && !is_vector;
}; };
/* Default: is_comparable -> std::false_type */ /* Default: is_comparable -> std::false_type */
template <typename T, typename SFINAE = void> template <typename T, typename SFINAE = void>
struct is_comparable : std::false_type { }; struct is_comparable : std::false_type {};
/* For non-map data structures, check whether operator== can be instantiated */ /* For non-map data structures, check whether operator== can be instantiated */
template <typename T> template <typename T>
struct is_comparable< struct is_comparable<
T, enable_if_t<container_traits<T>::is_element && T,
container_traits<T>::is_comparable>> enable_if_t<container_traits<T>::is_element && container_traits<T>::is_comparable>>
: std::true_type { }; : std::true_type {};
/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ /* For a vector/map data structure, recursively check the value type
(which is std::pair for maps) */
template <typename T> template <typename T>
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> { struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> {
static constexpr const bool value = static constexpr const bool value = is_comparable<typename T::value_type>::value;
is_comparable<typename T::value_type>::value;
}; };
/* For pairs, recursively check the two data types */ /* For pairs, recursively check the two data types */
template <typename T> template <typename T>
struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> { struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> {
static constexpr const bool value = static constexpr const bool value = is_comparable<typename T::first_type>::value
is_comparable<typename T::first_type>::value && && is_comparable<typename T::second_type>::value;
is_comparable<typename T::second_type>::value;
}; };
/* Fallback functions */ /* Fallback functions */
template <typename, typename, typename... Args> void vector_if_copy_constructible(const Args &...) { } template <typename, typename, typename... Args>
template <typename, typename, typename... Args> void vector_if_equal_operator(const Args &...) { } void vector_if_copy_constructible(const Args &...) {}
template <typename, typename, typename... Args> void vector_if_insertion_operator(const Args &...) { } template <typename, typename, typename... Args>
template <typename, typename, typename... Args> void vector_modifiers(const Args &...) { } void vector_if_equal_operator(const Args &...) {}
template <typename, typename, typename... Args>
void vector_if_insertion_operator(const Args &...) {}
template <typename, typename, typename... Args>
void vector_modifiers(const Args &...) {}
template<typename Vector, typename Class_> template <typename Vector, typename Class_>
void vector_if_copy_constructible(enable_if_t<is_copy_constructible<Vector>::value, Class_> &cl) { void vector_if_copy_constructible(enable_if_t<is_copy_constructible<Vector>::value, Class_> &cl) {
cl.def(init<const Vector &>(), "Copy constructor"); cl.def(init<const Vector &>(), "Copy constructor");
} }
template<typename Vector, typename Class_> template <typename Vector, typename Class_>
void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_> &cl) { void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_> &cl) {
using T = typename Vector::value_type; using T = typename Vector::value_type;
cl.def(self == self); cl.def(self == self);
cl.def(self != self); cl.def(self != self);
cl.def("count", cl.def(
[](const Vector &v, const T &x) { "count",
return std::count(v.begin(), v.end(), x); [](const Vector &v, const T &x) { return std::count(v.begin(), v.end(), x); },
},
arg("x"), arg("x"),
"Return the number of times ``x`` appears in the list" "Return the number of times ``x`` appears in the list");
);
cl.def("remove", [](Vector &v, const T &x) { cl.def(
"remove",
[](Vector &v, const T &x) {
auto p = std::find(v.begin(), v.end(), x); auto p = std::find(v.begin(), v.end(), x);
if (p != v.end()) if (p != v.end()) {
v.erase(p); v.erase(p);
else } else {
throw value_error(); throw value_error();
}
}, },
arg("x"), arg("x"),
"Remove the first item from the list whose value is x. " "Remove the first item from the list whose value is x. "
"It is an error if there is no such item." "It is an error if there is no such item.");
);
cl.def("__contains__", cl.def(
[](const Vector &v, const T &x) { "__contains__",
return std::find(v.begin(), v.end(), x) != v.end(); [](const Vector &v, const T &x) { return std::find(v.begin(), v.end(), x) != v.end(); },
},
arg("x"), arg("x"),
"Return true the container contains ``x``" "Return true the container contains ``x``");
);
} }
// Vector modifiers -- requires a copyable vector_type: // Vector modifiers -- requires a copyable vector_type:
// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems // (Technically, some of these (pop and __delitem__) don't actually require copyability, but it
// silly to allow deletion but not insertion, so include them here too.) // seems silly to allow deletion but not insertion, so include them here too.)
template <typename Vector, typename Class_> template <typename Vector, typename Class_>
void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) { void vector_modifiers(
enable_if_t<is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
using T = typename Vector::value_type; using T = typename Vector::value_type;
using SizeType = typename Vector::size_type; using SizeType = typename Vector::size_type;
using DiffType = typename Vector::difference_type; using DiffType = typename Vector::difference_type;
auto wrap_i = [](DiffType i, SizeType n) { auto wrap_i = [](DiffType i, SizeType n) {
if (i < 0) if (i < 0) {
i += n; i += n;
if (i < 0 || (SizeType)i >= n) }
if (i < 0 || (SizeType) i >= n) {
throw index_error(); throw index_error();
}
return i; return i;
}; };
cl.def("append", cl.def(
[](Vector &v, const T &value) { v.push_back(value); }, "append",
arg("x"), [](Vector &v, const T &value) { v.push_back(value); },
"Add an item to the end of the list"); arg("x"),
"Add an item to the end of the list");
cl.def(init([](const iterable &it) { cl.def(init([](const iterable &it) {
auto v = std::unique_ptr<Vector>(new Vector()); auto v = std::unique_ptr<Vector>(new Vector());
v->reserve(len_hint(it)); v->reserve(len_hint(it));
for (handle h : it) for (handle h : it) {
v->push_back(h.cast<T>()); v->push_back(h.cast<T>());
}
return v.release(); return v.release();
})); }));
cl.def("clear", cl.def(
[](Vector &v) { "clear", [](Vector &v) { v.clear(); }, "Clear the contents");
v.clear();
},
"Clear the contents"
);
cl.def("extend", cl.def(
[](Vector &v, const Vector &src) { "extend",
v.insert(v.end(), src.begin(), src.end()); [](Vector &v, const Vector &src) { v.insert(v.end(), src.begin(), src.end()); },
}, arg("L"),
arg("L"), "Extend the list by appending all the items in the given list");
"Extend the list by appending all the items in the given list"
);
cl.def( cl.def(
"extend", "extend",
@ -174,31 +185,36 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
arg("L"), arg("L"),
"Extend the list by appending all the items in the given list"); "Extend the list by appending all the items in the given list");
cl.def("insert", cl.def(
"insert",
[](Vector &v, DiffType i, const T &x) { [](Vector &v, DiffType i, const T &x) {
// Can't use wrap_i; i == v.size() is OK // Can't use wrap_i; i == v.size() is OK
if (i < 0) if (i < 0) {
i += v.size(); i += v.size();
if (i < 0 || (SizeType)i > v.size()) }
if (i < 0 || (SizeType) i > v.size()) {
throw index_error(); throw index_error();
}
v.insert(v.begin() + i, x); v.insert(v.begin() + i, x);
}, },
arg("i") , arg("x"), arg("i"),
"Insert an item at a given position." arg("x"),
); "Insert an item at a given position.");
cl.def("pop", cl.def(
"pop",
[](Vector &v) { [](Vector &v) {
if (v.empty()) if (v.empty()) {
throw index_error(); throw index_error();
}
T t = std::move(v.back()); T t = std::move(v.back());
v.pop_back(); v.pop_back();
return t; return t;
}, },
"Remove and return the last item" "Remove and return the last item");
);
cl.def("pop", cl.def(
"pop",
[wrap_i](Vector &v, DiffType i) { [wrap_i](Vector &v, DiffType i) {
i = wrap_i(i, v.size()); i = wrap_i(i, v.size());
T t = std::move(v[(SizeType) i]); T t = std::move(v[(SizeType) i]);
@ -206,15 +222,12 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
return t; return t;
}, },
arg("i"), arg("i"),
"Remove and return the item at index ``i``" "Remove and return the item at index ``i``");
);
cl.def("__setitem__", cl.def("__setitem__", [wrap_i](Vector &v, DiffType i, const T &t) {
[wrap_i](Vector &v, DiffType i, const T &t) { i = wrap_i(i, v.size());
i = wrap_i(i, v.size()); v[(SizeType) i] = t;
v[(SizeType)i] = t; });
}
);
/// Slicing protocol /// Slicing protocol
cl.def( cl.def(
@ -222,13 +235,14 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
[](const Vector &v, slice slice) -> Vector * { [](const Vector &v, slice slice) -> Vector * {
size_t start = 0, stop = 0, step = 0, slicelength = 0; size_t start = 0, stop = 0, step = 0, slicelength = 0;
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
throw error_already_set(); throw error_already_set();
}
auto *seq = new Vector(); auto *seq = new Vector();
seq->reserve((size_t) slicelength); seq->reserve((size_t) slicelength);
for (size_t i=0; i<slicelength; ++i) { for (size_t i = 0; i < slicelength; ++i) {
seq->push_back(v[start]); seq->push_back(v[start]);
start += step; start += step;
} }
@ -241,34 +255,38 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
"__setitem__", "__setitem__",
[](Vector &v, slice slice, const Vector &value) { [](Vector &v, slice slice, const Vector &value) {
size_t start = 0, stop = 0, step = 0, slicelength = 0; size_t start = 0, stop = 0, step = 0, slicelength = 0;
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
throw error_already_set(); throw error_already_set();
}
if (slicelength != value.size()) if (slicelength != value.size()) {
throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); throw std::runtime_error(
"Left and right hand size of slice assignment have different sizes!");
}
for (size_t i=0; i<slicelength; ++i) { for (size_t i = 0; i < slicelength; ++i) {
v[start] = value[i]; v[start] = value[i];
start += step; start += step;
} }
}, },
"Assign list elements using a slice object"); "Assign list elements using a slice object");
cl.def("__delitem__", cl.def(
"__delitem__",
[wrap_i](Vector &v, DiffType i) { [wrap_i](Vector &v, DiffType i) {
i = wrap_i(i, v.size()); i = wrap_i(i, v.size());
v.erase(v.begin() + i); v.erase(v.begin() + i);
}, },
"Delete the list elements at index ``i``" "Delete the list elements at index ``i``");
);
cl.def( cl.def(
"__delitem__", "__delitem__",
[](Vector &v, slice slice) { [](Vector &v, slice slice) {
size_t start = 0, stop = 0, step = 0, slicelength = 0; size_t start = 0, stop = 0, step = 0, slicelength = 0;
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
throw error_already_set(); throw error_already_set();
}
if (step == 1 && false) { if (step == 1 && false) {
v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength));
@ -284,8 +302,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
// If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>), // If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>),
// we have to access by copying; otherwise we return by reference. // we have to access by copying; otherwise we return by reference.
template <typename Vector> using vector_needs_copy = negation< template <typename Vector>
std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>>; using vector_needs_copy
= negation<std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]),
typename Vector::value_type &>>;
// The usual case: access and iterate by reference // The usual case: access and iterate by reference
template <typename Vector, typename Class_> template <typename Vector, typename Class_>
@ -293,31 +313,34 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl)
using T = typename Vector::value_type; using T = typename Vector::value_type;
using SizeType = typename Vector::size_type; using SizeType = typename Vector::size_type;
using DiffType = typename Vector::difference_type; using DiffType = typename Vector::difference_type;
using ItType = typename Vector::iterator; using ItType = typename Vector::iterator;
auto wrap_i = [](DiffType i, SizeType n) { auto wrap_i = [](DiffType i, SizeType n) {
if (i < 0) if (i < 0) {
i += n; i += n;
if (i < 0 || (SizeType)i >= n) }
if (i < 0 || (SizeType) i >= n) {
throw index_error(); throw index_error();
}
return i; return i;
}; };
cl.def("__getitem__", cl.def(
"__getitem__",
[wrap_i](Vector &v, DiffType i) -> T & { [wrap_i](Vector &v, DiffType i) -> T & {
i = wrap_i(i, v.size()); i = wrap_i(i, v.size());
return v[(SizeType)i]; return v[(SizeType) i];
}, },
return_value_policy::reference_internal // ref + keepalive return_value_policy::reference_internal // ref + keepalive
); );
cl.def("__iter__", cl.def(
[](Vector &v) { "__iter__",
return make_iterator< [](Vector &v) {
return_value_policy::reference_internal, ItType, ItType, T&>( return make_iterator<return_value_policy::reference_internal, ItType, ItType, T &>(
v.begin(), v.end()); v.begin(), v.end());
}, },
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
); );
} }
@ -327,53 +350,60 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl)
using T = typename Vector::value_type; using T = typename Vector::value_type;
using SizeType = typename Vector::size_type; using SizeType = typename Vector::size_type;
using DiffType = typename Vector::difference_type; using DiffType = typename Vector::difference_type;
using ItType = typename Vector::iterator; using ItType = typename Vector::iterator;
cl.def("__getitem__", cl.def("__getitem__", [](const Vector &v, DiffType i) -> T {
[](const Vector &v, DiffType i) -> T { if (i < 0 && (i += v.size()) < 0) {
if (i < 0 && (i += v.size()) < 0) throw index_error();
throw index_error();
if ((SizeType)i >= v.size())
throw index_error();
return v[(SizeType)i];
} }
); if ((SizeType) i >= v.size()) {
throw index_error();
}
return v[(SizeType) i];
});
cl.def("__iter__", cl.def(
[](Vector &v) { "__iter__",
return make_iterator< [](Vector &v) {
return_value_policy::copy, ItType, ItType, T>( return make_iterator<return_value_policy::copy, ItType, ItType, T>(v.begin(), v.end());
v.begin(), v.end()); },
}, keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
); );
} }
template <typename Vector, typename Class_> auto vector_if_insertion_operator(Class_ &cl, std::string const &name) template <typename Vector, typename Class_>
-> decltype(std::declval<std::ostream&>() << std::declval<typename Vector::value_type>(), void()) { auto vector_if_insertion_operator(Class_ &cl, std::string const &name)
-> decltype(std::declval<std::ostream &>() << std::declval<typename Vector::value_type>(),
void()) {
using size_type = typename Vector::size_type; using size_type = typename Vector::size_type;
cl.def("__repr__", cl.def(
[name](Vector &v) { "__repr__",
[name](Vector &v) {
std::ostringstream s; std::ostringstream s;
s << name << '['; s << name << '[';
for (size_type i=0; i < v.size(); ++i) { for (size_type i = 0; i < v.size(); ++i) {
s << v[i]; s << v[i];
if (i != v.size() - 1) if (i != v.size() - 1) {
s << ", "; s << ", ";
}
} }
s << ']'; s << ']';
return s.str(); return s.str();
}, },
"Return the canonical string representation of this list." "Return the canonical string representation of this list.");
);
} }
// Provide the buffer interface for vectors if we have data() and we have a format for it // Provide the buffer interface for vectors if we have data() and we have a format for it
// GCC seems to have "void std::vector<bool>::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer // GCC seems to have "void std::vector<bool>::data()" - doing SFINAE on the existence of data()
// is insufficient, we need to check it returns an appropriate pointer
template <typename Vector, typename = void> template <typename Vector, typename = void>
struct vector_has_data_and_format : std::false_type {}; struct vector_has_data_and_format : std::false_type {};
template <typename Vector> template <typename Vector>
struct vector_has_data_and_format<Vector, enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(), std::declval<Vector>().data()), typename Vector::value_type*>::value>> : std::true_type {}; struct vector_has_data_and_format<
Vector,
enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(),
std::declval<Vector>().data()),
typename Vector::value_type *>::value>> : std::true_type {};
// [workaround(intel)] Separate function required here // [workaround(intel)] Separate function required here
// Workaround as the Intel compiler does not compile the enable_if_t part below // Workaround as the Intel compiler does not compile the enable_if_t part below
@ -388,26 +418,37 @@ constexpr bool args_any_are_buffer() {
// Add the buffer interface to a vector // Add the buffer interface to a vector
template <typename Vector, typename Class_, typename... Args> template <typename Vector, typename Class_, typename... Args>
void vector_buffer_impl(Class_& cl, std::true_type) { void vector_buffer_impl(Class_ &cl, std::true_type) {
using T = typename Vector::value_type; using T = typename Vector::value_type;
static_assert(vector_has_data_and_format<Vector>::value, "There is not an appropriate format descriptor for this vector"); static_assert(vector_has_data_and_format<Vector>::value,
"There is not an appropriate format descriptor for this vector");
// numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard
// at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here
format_descriptor<T>::format(); format_descriptor<T>::format();
cl.def_buffer([](Vector& v) -> buffer_info { cl.def_buffer([](Vector &v) -> buffer_info {
return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)}); return buffer_info(v.data(),
static_cast<ssize_t>(sizeof(T)),
format_descriptor<T>::format(),
1,
{v.size()},
{sizeof(T)});
}); });
cl.def(init([](const buffer &buf) { cl.def(init([](const buffer &buf) {
auto info = buf.request(); auto info = buf.request();
if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T))) if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T))) {
throw type_error("Only valid 1D buffers can be copied to a vector"); throw type_error("Only valid 1D buffers can be copied to a vector");
if (!detail::compare_buffer_info<T>::compare(info) || (ssize_t) sizeof(T) != info.itemsize) }
throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor<T>::format() + ")"); if (!detail::compare_buffer_info<T>::compare(info)
|| (ssize_t) sizeof(T) != info.itemsize) {
throw type_error("Format mismatch (Python: " + info.format
+ " C++: " + format_descriptor<T>::format() + ")");
}
T *p = static_cast<T*>(info.ptr); T *p = static_cast<T *>(info.ptr);
ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T)); ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
T *end = p + info.shape[0] * step; T *end = p + info.shape[0] * step;
if (step == 1) { if (step == 1) {
@ -415,21 +456,22 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
} }
Vector vec; Vector vec;
vec.reserve((size_t) info.shape[0]); vec.reserve((size_t) info.shape[0]);
for (; p != end; p += step) for (; p != end; p += step) {
vec.push_back(*p); vec.push_back(*p);
}
return vec; return vec;
})); }));
return; return;
} }
template <typename Vector, typename Class_, typename... Args> template <typename Vector, typename Class_, typename... Args>
void vector_buffer_impl(Class_&, std::false_type) {} void vector_buffer_impl(Class_ &, std::false_type) {}
template <typename Vector, typename Class_, typename... Args> template <typename Vector, typename Class_, typename... Args>
void vector_buffer(Class_& cl) { void vector_buffer(Class_ &cl) {
vector_buffer_impl<Vector, Class_, Args...>(cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{}); vector_buffer_impl<Vector, Class_, Args...>(
cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{});
} }
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
@ -438,13 +480,13 @@ PYBIND11_NAMESPACE_END(detail)
// std::vector // std::vector
// //
template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args> template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, Args&&... args) { class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, Args &&...args) {
using Class_ = class_<Vector, holder_type>; using Class_ = class_<Vector, holder_type>;
// If the value_type is unregistered (e.g. a converting type) or is itself registered // If the value_type is unregistered (e.g. a converting type) or is itself registered
// module-local then make the vector binding module-local as well: // module-local then make the vector binding module-local as well:
using vtype = typename Vector::value_type; using vtype = typename Vector::value_type;
auto vtype_info = detail::get_type_info(typeid(vtype)); auto *vtype_info = detail::get_type_info(typeid(vtype));
bool local = !vtype_info || vtype_info->module_local; bool local = !vtype_info || vtype_info->module_local;
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...); Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
@ -469,18 +511,13 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
// Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive
detail::vector_accessor<Vector, Class_>(cl); detail::vector_accessor<Vector, Class_>(cl);
cl.def("__bool__", cl.def(
[](const Vector &v) -> bool { "__bool__",
return !v.empty(); [](const Vector &v) -> bool { return !v.empty(); },
}, "Check whether the list is nonempty");
"Check whether the list is nonempty"
);
cl.def("__len__", &Vector::size); cl.def("__len__", &Vector::size);
#if 0 #if 0
// C++ style functions deprecated, leaving it here as an example // C++ style functions deprecated, leaving it here as an example
cl.def(init<size_type>()); cl.def(init<size_type>());
@ -524,8 +561,6 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
return cl; return cl;
} }
// //
// std::map, std::unordered_map // std::map, std::unordered_map
// //
@ -533,81 +568,104 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
/* Fallback functions */ /* Fallback functions */
template <typename, typename, typename... Args> void map_if_insertion_operator(const Args &...) { } template <typename, typename, typename... Args>
template <typename, typename, typename... Args> void map_assignment(const Args &...) { } void map_if_insertion_operator(const Args &...) {}
template <typename, typename, typename... Args>
void map_assignment(const Args &...) {}
// Map assignment when copy-assignable: just copy the value // Map assignment when copy-assignable: just copy the value
template <typename Map, typename Class_> template <typename Map, typename Class_>
void map_assignment(enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) { void map_assignment(
enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
using KeyType = typename Map::key_type; using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type; using MappedType = typename Map::mapped_type;
cl.def("__setitem__", cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
[](Map &m, const KeyType &k, const MappedType &v) { auto it = m.find(k);
auto it = m.find(k); if (it != m.end()) {
if (it != m.end()) it->second = v; it->second = v;
else m.emplace(k, v); } else {
} m.emplace(k, v);
); }
});
} }
// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting // Not copy-assignable, but still copy-constructible: we can update the value by erasing and
template<typename Map, typename Class_> // reinserting
void map_assignment(enable_if_t< template <typename Map, typename Class_>
!is_copy_assignable<typename Map::mapped_type>::value && void map_assignment(enable_if_t<!is_copy_assignable<typename Map::mapped_type>::value
is_copy_constructible<typename Map::mapped_type>::value, && is_copy_constructible<typename Map::mapped_type>::value,
Class_> &cl) { Class_> &cl) {
using KeyType = typename Map::key_type; using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type; using MappedType = typename Map::mapped_type;
cl.def("__setitem__", cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
[](Map &m, const KeyType &k, const MappedType &v) { // We can't use m[k] = v; because value type might not be default constructable
// We can't use m[k] = v; because value type might not be default constructable auto r = m.emplace(k, v);
auto r = m.emplace(k, v); if (!r.second) {
if (!r.second) { // value type is not copy assignable so the only way to insert it is to erase it
// value type is not copy assignable so the only way to insert it is to erase it first... // first...
m.erase(r.first); m.erase(r.first);
m.emplace(k, v); m.emplace(k, v);
} }
} });
);
} }
template <typename Map, typename Class_>
auto map_if_insertion_operator(Class_ &cl, std::string const &name)
-> decltype(std::declval<std::ostream &>() << std::declval<typename Map::key_type>()
<< std::declval<typename Map::mapped_type>(),
void()) {
template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &cl, std::string const &name) cl.def(
-> decltype(std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void()) { "__repr__",
[name](Map &m) {
cl.def("__repr__",
[name](Map &m) {
std::ostringstream s; std::ostringstream s;
s << name << '{'; s << name << '{';
bool f = false; bool f = false;
for (auto const &kv : m) { for (auto const &kv : m) {
if (f) if (f) {
s << ", "; s << ", ";
}
s << kv.first << ": " << kv.second; s << kv.first << ": " << kv.second;
f = true; f = true;
} }
s << '}'; s << '}';
return s.str(); return s.str();
}, },
"Return the canonical string representation of this map." "Return the canonical string representation of this map.");
);
} }
template <typename Map>
struct keys_view {
Map &map;
};
template <typename Map>
struct values_view {
Map &map;
};
template <typename Map>
struct items_view {
Map &map;
};
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args> template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&... args) { class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&...args) {
using KeyType = typename Map::key_type; using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type; using MappedType = typename Map::mapped_type;
using KeysView = detail::keys_view<Map>;
using ValuesView = detail::values_view<Map>;
using ItemsView = detail::items_view<Map>;
using Class_ = class_<Map, holder_type>; using Class_ = class_<Map, holder_type>;
// If either type is a non-module-local bound type then make the map binding non-local as well; // If either type is a non-module-local bound type then make the map binding non-local as well;
// otherwise (e.g. both types are either module-local or converting) the map will be // otherwise (e.g. both types are either module-local or converting) the map will be
// module-local. // module-local.
auto tinfo = detail::get_type_info(typeid(MappedType)); auto *tinfo = detail::get_type_info(typeid(MappedType));
bool local = !tinfo || tinfo->module_local; bool local = !tinfo || tinfo->module_local;
if (local) { if (local) {
tinfo = detail::get_type_info(typeid(KeyType)); tinfo = detail::get_type_info(typeid(KeyType));
@ -615,60 +673,112 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
} }
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...); Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
class_<KeysView> keys_view(
scope, ("KeysView[" + name + "]").c_str(), pybind11::module_local(local));
class_<ValuesView> values_view(
scope, ("ValuesView[" + name + "]").c_str(), pybind11::module_local(local));
class_<ItemsView> items_view(
scope, ("ItemsView[" + name + "]").c_str(), pybind11::module_local(local));
cl.def(init<>()); cl.def(init<>());
// Register stream insertion operator (if possible) // Register stream insertion operator (if possible)
detail::map_if_insertion_operator<Map, Class_>(cl, name); detail::map_if_insertion_operator<Map, Class_>(cl, name);
cl.def("__bool__", cl.def(
"__bool__",
[](const Map &m) -> bool { return !m.empty(); }, [](const Map &m) -> bool { return !m.empty(); },
"Check whether the map is nonempty" "Check whether the map is nonempty");
cl.def(
"__iter__",
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
); );
cl.def("__iter__", cl.def(
[](Map &m) { return make_key_iterator(m.begin(), m.end()); }, "keys",
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ [](Map &m) { return KeysView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def("items", cl.def(
[](Map &m) { return make_iterator(m.begin(), m.end()); }, "values",
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ [](Map &m) { return ValuesView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def("__getitem__", cl.def(
"items",
[](Map &m) { return ItemsView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);
cl.def(
"__getitem__",
[](Map &m, const KeyType &k) -> MappedType & { [](Map &m, const KeyType &k) -> MappedType & {
auto it = m.find(k); auto it = m.find(k);
if (it == m.end()) if (it == m.end()) {
throw key_error(); throw key_error();
return it->second; }
return it->second;
}, },
return_value_policy::reference_internal // ref + keepalive return_value_policy::reference_internal // ref + keepalive
); );
cl.def("__contains__", cl.def("__contains__", [](Map &m, const KeyType &k) -> bool {
[](Map &m, const KeyType &k) -> bool { auto it = m.find(k);
auto it = m.find(k); if (it == m.end()) {
if (it == m.end()) return false;
return false;
return true;
} }
); return true;
});
// Fallback for when the object is not of the key type
cl.def("__contains__", [](Map &, const object &) -> bool { return false; });
// Assignment provided only if the type is copyable // Assignment provided only if the type is copyable
detail::map_assignment<Map, Class_>(cl); detail::map_assignment<Map, Class_>(cl);
cl.def("__delitem__", cl.def("__delitem__", [](Map &m, const KeyType &k) {
[](Map &m, const KeyType &k) { auto it = m.find(k);
auto it = m.find(k); if (it == m.end()) {
if (it == m.end()) throw key_error();
throw key_error(); }
m.erase(it); m.erase(it);
} });
);
cl.def("__len__", &Map::size); cl.def("__len__", &Map::size);
keys_view.def("__len__", [](KeysView &view) { return view.map.size(); });
keys_view.def(
"__iter__",
[](KeysView &view) { return make_key_iterator(view.map.begin(), view.map.end()); },
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
keys_view.def("__contains__", [](KeysView &view, const KeyType &k) -> bool {
auto it = view.map.find(k);
if (it == view.map.end()) {
return false;
}
return true;
});
// Fallback for when the object is not of the key type
keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; });
values_view.def("__len__", [](ValuesView &view) { return view.map.size(); });
values_view.def(
"__iter__",
[](ValuesView &view) { return make_value_iterator(view.map.begin(), view.map.end()); },
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
items_view.def("__len__", [](ItemsView &view) { return view.map.size(); });
items_view.def(
"__iter__",
[](ItemsView &view) { return make_iterator(view.map.begin(), view.map.end()); },
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
return cl; return cl;
} }

92
thirdparty/pybind11/noxfile.py vendored Normal file
View File

@ -0,0 +1,92 @@
import nox
nox.needs_version = ">=2022.1.7"
nox.options.sessions = ["lint", "tests", "tests_packaging"]
PYTHON_VERSIONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
@nox.session(reuse_venv=True)
def lint(session: nox.Session) -> None:
"""
Lint the codebase (except for clang-format/tidy).
"""
session.install("pre-commit")
session.run("pre-commit", "run", "-a")
@nox.session(python=PYTHON_VERSIONS)
def tests(session: nox.Session) -> None:
"""
Run the tests (requires a compiler).
"""
tmpdir = session.create_tmp()
session.install("cmake")
session.install("-r", "tests/requirements.txt")
session.run(
"cmake",
"-S.",
f"-B{tmpdir}",
"-DPYBIND11_WERROR=ON",
"-DDOWNLOAD_CATCH=ON",
"-DDOWNLOAD_EIGEN=ON",
*session.posargs,
)
session.run("cmake", "--build", tmpdir)
session.run("cmake", "--build", tmpdir, "--config=Release", "--target", "check")
@nox.session
def tests_packaging(session: nox.Session) -> None:
"""
Run the packaging tests.
"""
session.install("-r", "tests/requirements.txt", "--prefer-binary")
session.run("pytest", "tests/extra_python_package")
@nox.session(reuse_venv=True)
def docs(session: nox.Session) -> None:
"""
Build the docs. Pass "serve" to serve.
"""
session.install("-r", "docs/requirements.txt")
session.chdir("docs")
if "pdf" in session.posargs:
session.run("sphinx-build", "-b", "latexpdf", ".", "_build")
return
session.run("sphinx-build", "-b", "html", ".", "_build")
if "serve" in session.posargs:
session.log("Launching docs at http://localhost:8000/ - use Ctrl-C to quit")
session.run("python", "-m", "http.server", "8000", "-d", "_build/html")
elif session.posargs:
session.error("Unsupported argument to docs")
@nox.session(reuse_venv=True)
def make_changelog(session: nox.Session) -> None:
"""
Inspect the closed issues and make entries for a changelog.
"""
session.install("ghapi", "rich")
session.run("python", "tools/make_changelog.py")
@nox.session(reuse_venv=True)
def build(session: nox.Session) -> None:
"""
Build SDists and wheels.
"""
session.install("build")
session.log("Building normal files")
session.run("python", "-m", "build", *session.posargs)
session.log("Building pybind11-global files (PYBIND11_GLOBAL_SDIST=1)")
session.run(
"python", "-m", "build", *session.posargs, env={"PYBIND11_GLOBAL_SDIST": "1"}
)

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ._version import version_info, __version__ from ._version import __version__, version_info
from .commands import get_include, get_cmake_dir from .commands import get_cmake_dir, get_include
__all__ = ( __all__ = (
"version_info", "version_info",

View File

@ -5,7 +5,7 @@ import argparse
import sys import sys
import sysconfig import sysconfig
from .commands import get_include, get_cmake_dir from .commands import get_cmake_dir, get_include
def print_includes(): def print_includes():

View File

@ -8,5 +8,5 @@ def _to_int(s):
return s return s
__version__ = "2.7.1" __version__ = "2.9.2"
version_info = tuple(_to_int(s) for s in __version__.split(".")) version_info = tuple(_to_int(s) for s in __version__.split("."))

View File

@ -1,4 +1,4 @@
from typing import Union, Tuple from typing import Tuple, Union
def _to_int(s: str) -> Union[int, str]: ... def _to_int(s: str) -> Union[int, str]: ...

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
DIR = os.path.abspath(os.path.dirname(__file__)) DIR = os.path.abspath(os.path.dirname(__file__))

View File

@ -41,23 +41,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import contextlib import contextlib
import os import os
import platform
import shlex
import shutil import shutil
import sys import sys
import sysconfig
import tempfile import tempfile
import threading import threading
import platform
import warnings import warnings
import sysconfig
try: try:
from setuptools.command.build_ext import build_ext as _build_ext
from setuptools import Extension as _Extension from setuptools import Extension as _Extension
from setuptools.command.build_ext import build_ext as _build_ext
except ImportError: except ImportError:
from distutils.command.build_ext import build_ext as _build_ext from distutils.command.build_ext import build_ext as _build_ext
from distutils.extension import Extension as _Extension from distutils.extension import Extension as _Extension
import distutils.errors
import distutils.ccompiler import distutils.ccompiler
import distutils.errors
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform() WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
PY2 = sys.version_info[0] < 3 PY2 = sys.version_info[0] < 3
@ -143,7 +144,12 @@ class Pybind11Extension(_Extension):
if WIN: if WIN:
cflags += ["/EHsc", "/bigobj"] cflags += ["/EHsc", "/bigobj"]
else: else:
cflags += ["-fvisibility=hidden", "-g0"] cflags += ["-fvisibility=hidden"]
env_cflags = os.environ.get("CFLAGS", "")
env_cppflags = os.environ.get("CPPFLAGS", "")
c_cpp_flags = shlex.split(env_cflags) + shlex.split(env_cppflags)
if not any(opt.startswith("-g") for opt in c_cpp_flags):
cflags += ["-g0"]
if MACOS: if MACOS:
cflags += ["-stdlib=libc++"] cflags += ["-stdlib=libc++"]
ldflags += ["-stdlib=libc++"] ldflags += ["-stdlib=libc++"]
@ -460,8 +466,14 @@ class ParallelCompile(object):
threads = 1 threads = 1
if threads > 1: if threads > 1:
for _ in ThreadPool(threads).imap_unordered(_single_compile, objects): pool = ThreadPool(threads)
pass # In Python 2, ThreadPool can't be used as a context manager.
# Once we are no longer supporting it, this can be 'with pool:'
try:
for _ in pool.imap_unordered(_single_compile, objects):
pass
finally:
pool.terminate()
else: else:
for ob in objects: for ob in objects:
_single_compile(ob) _single_compile(ob)

View File

@ -1,13 +1,12 @@
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI / # IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
# pre-commit). # pre-commit).
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union import contextlib
from types import TracebackType import distutils.ccompiler
from distutils.command.build_ext import build_ext as _build_ext # type: ignore from distutils.command.build_ext import build_ext as _build_ext # type: ignore
from distutils.extension import Extension as _Extension from distutils.extension import Extension as _Extension
import distutils.ccompiler from types import TracebackType
import contextlib from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
WIN: bool WIN: bool
PY2: bool PY2: bool

View File

@ -1,3 +1,41 @@
[build-system] [build-system]
requires = ["setuptools>=42", "wheel", "cmake>=3.18", "ninja"] requires = ["setuptools>=42", "wheel", "cmake>=3.18", "ninja"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[tool.check-manifest]
ignore = [
"tests/**",
"docs/**",
"tools/**",
"include/**",
".*",
"pybind11/include/**",
"pybind11/share/**",
"CMakeLists.txt",
"noxfile.py",
]
[tool.isort]
# Needs the compiled .so modules and env.py from tests
known_first_party = "env,pybind11_cross_module_tests,pybind11_tests,"
# For black compatibility
profile = "black"
[tool.mypy]
files = "pybind11"
python_version = "2.7"
warn_unused_configs = true
disallow_any_generics = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
no_implicit_reexport = true
strict_equality = true

View File

@ -20,6 +20,7 @@ classifiers =
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
License :: OSI Approved :: BSD License License :: OSI Approved :: BSD License
Programming Language :: Python :: Implementation :: PyPy Programming Language :: Python :: Implementation :: PyPy
Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: CPython
@ -30,24 +31,20 @@ keywords =
C++11 C++11
Python bindings Python bindings
project_urls =
Documentation = https://pybind11.readthedocs.io/
Bug Tracker = https://github.com/pybind/pybind11/issues
Discussions = https://github.com/pybind/pybind11/discussions
Changelog = https://pybind11.readthedocs.io/en/latest/changelog.html
Chat = https://gitter.im/pybind/Lobby
[options] [options]
python_requires = >=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4 python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
zip_safe = False zip_safe = False
[bdist_wheel] [bdist_wheel]
universal=1 universal=1
[check-manifest]
ignore =
tests/**
docs/**
tools/**
include/**
.*
pybind11/include/**
pybind11/share/**
CMakeLists.txt
[flake8] [flake8]
max-line-length = 99 max-line-length = 99
@ -61,25 +58,6 @@ ignore =
# Black conflict # Black conflict
W503, E203 W503, E203
[mypy]
files = pybind11
python_version = 2.7
warn_unused_configs = True
# Currently (0.800) identical to --strict
disallow_any_generics = True
disallow_subclassing_any = True
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
check_untyped_defs = True
disallow_untyped_decorators = True
no_implicit_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
warn_return_any = True
no_implicit_reexport = True
strict_equality = True
[tool:pytest] [tool:pytest]
timeout = 300 timeout = 300

View File

@ -4,6 +4,7 @@
# Setup script for PyPI; use CMakeFile.txt to build extension modules # Setup script for PyPI; use CMakeFile.txt to build extension modules
import contextlib import contextlib
import io
import os import os
import re import re
import shutil import shutil
@ -19,6 +20,39 @@ VERSION_REGEX = re.compile(
r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE
) )
def build_expected_version_hex(matches):
patch_level_serial = matches["PATCH"]
serial = None
try:
major = int(matches["MAJOR"])
minor = int(matches["MINOR"])
flds = patch_level_serial.split(".")
if flds:
patch = int(flds[0])
level = None
if len(flds) == 1:
level = "0"
serial = 0
elif len(flds) == 2:
level_serial = flds[1]
for level in ("a", "b", "c", "dev"):
if level_serial.startswith(level):
serial = int(level_serial[len(level) :])
break
except ValueError:
pass
if serial is None:
msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial)
raise RuntimeError(msg)
return (
"0x"
+ "{:02x}{:02x}{:02x}{}{:x}".format(
major, minor, patch, level[:1], serial
).upper()
)
# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers # PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
# files, and the sys.prefix files (CMake and headers). # files, and the sys.prefix files (CMake and headers).
@ -40,7 +74,7 @@ exec(code, loc)
version = loc["__version__"] version = loc["__version__"]
# Verify that the version matches the one in C++ # Verify that the version matches the one in C++
with open("include/pybind11/detail/common.h") as f: with io.open("include/pybind11/detail/common.h", encoding="utf8") as f:
matches = dict(VERSION_REGEX.findall(f.read())) matches = dict(VERSION_REGEX.findall(f.read()))
cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches) cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
if version != cpp_version: if version != cpp_version:
@ -49,6 +83,15 @@ if version != cpp_version:
) )
raise RuntimeError(msg) raise RuntimeError(msg)
version_hex = matches.get("HEX", "MISSING")
expected_version_hex = build_expected_version_hex(matches)
if version_hex != expected_version_hex:
msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format(
version_hex,
expected_version_hex,
)
raise RuntimeError(msg)
def get_and_replace(filename, binary=False, **opts): def get_and_replace(filename, binary=False, **opts):
with open(filename, "rb" if binary else "r") as f: with open(filename, "rb" if binary else "r") as f:
@ -106,6 +149,13 @@ with remove_output("pybind11/include", "pybind11/share"):
"-DBUILD_TESTING=OFF", "-DBUILD_TESTING=OFF",
"-DPYBIND11_NOPYTHON=ON", "-DPYBIND11_NOPYTHON=ON",
] ]
if "CMAKE_ARGS" in os.environ:
fcommand = [
c
for c in os.environ["CMAKE_ARGS"].split()
if "DCMAKE_INSTALL_PREFIX" not in c
]
cmd += fcommand
cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr) cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
subprocess.check_call(cmd, **cmake_opts) subprocess.check_call(cmd, **cmake_opts)
subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts) subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts)

View File

@ -10,16 +10,16 @@ cmake_minimum_required(VERSION 3.4)
# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with # The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate # some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround: # the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.18) if(${CMAKE_VERSION} VERSION_LESS 3.21)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else() else()
cmake_policy(VERSION 3.18) cmake_policy(VERSION 3.21)
endif() endif()
# Only needed for CMake < 3.5 support # Only needed for CMake < 3.5 support
include(CMakeParseArguments) include(CMakeParseArguments)
# Filter out items; print an optional message if any items filtered # Filter out items; print an optional message if any items filtered. This ignores extensions.
# #
# Usage: # Usage:
# pybind11_filter_tests(LISTNAME file1.cpp file2.cpp ... MESSAGE "") # pybind11_filter_tests(LISTNAME file1.cpp file2.cpp ... MESSAGE "")
@ -27,10 +27,17 @@ include(CMakeParseArguments)
macro(pybind11_filter_tests LISTNAME) macro(pybind11_filter_tests LISTNAME)
cmake_parse_arguments(ARG "" "MESSAGE" "" ${ARGN}) cmake_parse_arguments(ARG "" "MESSAGE" "" ${ARGN})
set(PYBIND11_FILTER_TESTS_FOUND OFF) set(PYBIND11_FILTER_TESTS_FOUND OFF)
# Make a list of the test without any extensions, for easier filtering.
set(_TMP_ACTUAL_LIST "${${LISTNAME}};") # enforce ';' at the end to allow matching last item.
string(REGEX REPLACE "\\.[^.;]*;" ";" LIST_WITHOUT_EXTENSIONS "${_TMP_ACTUAL_LIST}")
foreach(filename IN LISTS ARG_UNPARSED_ARGUMENTS) foreach(filename IN LISTS ARG_UNPARSED_ARGUMENTS)
list(FIND ${LISTNAME} ${filename} _FILE_FOUND) string(REGEX REPLACE "\\.[^.]*$" "" filename_no_ext ${filename})
# Search in the list without extensions.
list(FIND LIST_WITHOUT_EXTENSIONS ${filename_no_ext} _FILE_FOUND)
if(_FILE_FOUND GREATER -1) if(_FILE_FOUND GREATER -1)
list(REMOVE_AT ${LISTNAME} ${_FILE_FOUND}) list(REMOVE_AT ${LISTNAME} ${_FILE_FOUND}) # And remove from the list with extensions.
list(REMOVE_AT LIST_WITHOUT_EXTENSIONS ${_FILE_FOUND}
)# And our search list, to ensure it is in sync.
set(PYBIND11_FILTER_TESTS_FOUND ON) set(PYBIND11_FILTER_TESTS_FOUND ON)
endif() endif()
endforeach() endforeach()
@ -47,6 +54,18 @@ macro(possibly_uninitialized)
endforeach() endforeach()
endmacro() endmacro()
# Function to add additional targets if any of the provided tests are found.
# Needles; Specifies the test names to look for.
# Additions; Specifies the additional test targets to add when any of the needles are found.
macro(tests_extra_targets needles additions)
# Add the index for this relation to the index extra targets map.
list(LENGTH PYBIND11_TEST_EXTRA_TARGETS PYBIND11_TEST_EXTRA_TARGETS_LEN)
list(APPEND PYBIND11_TEST_EXTRA_TARGETS ${PYBIND11_TEST_EXTRA_TARGETS_LEN})
# Add the test names to look for, and the associated test target additions.
set(PYBIND11_TEST_EXTRA_TARGETS_NEEDLES_${PYBIND11_TEST_EXTRA_TARGETS_LEN} ${needles})
set(PYBIND11_TEST_EXTRA_TARGETS_ADDITION_${PYBIND11_TEST_EXTRA_TARGETS_LEN} ${additions})
endmacro()
# New Python support # New Python support
if(DEFINED Python_EXECUTABLE) if(DEFINED Python_EXECUTABLE)
set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}") set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}")
@ -92,52 +111,67 @@ if(PYBIND11_CUDA_TESTS)
set(CMAKE_CUDA_STANDARD_REQUIRED ON) set(CMAKE_CUDA_STANDARD_REQUIRED ON)
endif() endif()
# Full set of test files (you can override these; see below) # Full set of test files (you can override these; see below, overrides ignore extension)
# Any test that has no extension is both .py and .cpp, so 'foo' will add 'foo.cpp' and 'foo.py'.
# Any test that has an extension is exclusively that and handled as such.
set(PYBIND11_TEST_FILES set(PYBIND11_TEST_FILES
test_async.cpp test_async
test_buffers.cpp test_buffers
test_builtin_casters.cpp test_builtin_casters
test_call_policies.cpp test_call_policies
test_callbacks.cpp test_callbacks
test_chrono.cpp test_chrono
test_class.cpp test_class
test_constants_and_functions.cpp test_const_name
test_copy_move.cpp test_constants_and_functions
test_custom_type_casters.cpp test_copy_move
test_docstring_options.cpp test_custom_type_casters
test_eigen.cpp test_custom_type_setup
test_enum.cpp test_docstring_options
test_eval.cpp test_eigen
test_exceptions.cpp test_enum
test_factory_constructors.cpp test_eval
test_gil_scoped.cpp test_exceptions
test_iostream.cpp test_factory_constructors
test_kwargs_and_defaults.cpp test_gil_scoped
test_local_bindings.cpp test_iostream
test_methods_and_attributes.cpp test_kwargs_and_defaults
test_modules.cpp test_local_bindings
test_multiple_inheritance.cpp test_methods_and_attributes
test_numpy_array.cpp test_modules
test_numpy_dtypes.cpp test_multiple_inheritance
test_numpy_vectorize.cpp test_numpy_array
test_opaque_types.cpp test_numpy_dtypes
test_operator_overloading.cpp test_numpy_vectorize
test_pickling.cpp test_opaque_types
test_pytypes.cpp test_operator_overloading
test_sequences_and_iterators.cpp test_pickling
test_smart_ptr.cpp test_pytypes
test_stl.cpp test_sequences_and_iterators
test_stl_binders.cpp test_smart_ptr
test_tagbased_polymorphic.cpp test_stl
test_union.cpp test_stl_binders
test_virtual_functions.cpp) test_tagbased_polymorphic
test_thread
test_union
test_virtual_functions)
# Invoking cmake with something like: # Invoking cmake with something like:
# cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp" .. # cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp" ..
# lets you override the tests that get compiled and run. You can restore to all tests with: # lets you override the tests that get compiled and run. You can restore to all tests with:
# cmake -DPYBIND11_TEST_OVERRIDE= .. # cmake -DPYBIND11_TEST_OVERRIDE= ..
if(PYBIND11_TEST_OVERRIDE) if(PYBIND11_TEST_OVERRIDE)
set(PYBIND11_TEST_FILES ${PYBIND11_TEST_OVERRIDE}) # Instead of doing a direct override here, we iterate over the overrides without extension and
# match them against entries from the PYBIND11_TEST_FILES, anything that not matches goes into the filter list.
string(REGEX REPLACE "\\.[^.;]*;" ";" TEST_OVERRIDE_NO_EXT "${PYBIND11_TEST_OVERRIDE};")
string(REGEX REPLACE "\\.[^.;]*;" ";" TEST_FILES_NO_EXT "${PYBIND11_TEST_FILES};")
# This allows the override to be done with extensions, preserving backwards compatibility.
foreach(test_name ${TEST_FILES_NO_EXT})
if(NOT ${test_name} IN_LIST TEST_OVERRIDE_NO_EXT
)# If not in the whitelist, add to be filtered out.
list(APPEND PYBIND11_TEST_FILTER ${test_name})
endif()
endforeach()
endif() endif()
# You can also filter tests: # You can also filter tests:
@ -159,15 +193,46 @@ if(PYBIND11_CUDA_TESTS)
"Skipping test_constants_and_functions due to incompatible exception specifications") "Skipping test_constants_and_functions due to incompatible exception specifications")
endif() endif()
string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}") # Now that the test filtering is complete, we need to split the list into the test for PYTEST
# and the list for the cpp targets.
set(PYBIND11_CPPTEST_FILES "")
set(PYBIND11_PYTEST_FILES "")
foreach(test_name ${PYBIND11_TEST_FILES})
if(test_name MATCHES "\\.py$") # Ends in .py, purely python test.
list(APPEND PYBIND11_PYTEST_FILES ${test_name})
elseif(test_name MATCHES "\\.cpp$") # Ends in .cpp, purely cpp test.
list(APPEND PYBIND11_CPPTEST_FILES ${test_name})
elseif(NOT test_name MATCHES "\\.") # No extension specified, assume both, add extension.
list(APPEND PYBIND11_PYTEST_FILES ${test_name}.py)
list(APPEND PYBIND11_CPPTEST_FILES ${test_name}.cpp)
else()
message(WARNING "Unhanded test extension in test: ${test_name}")
endif()
endforeach()
set(PYBIND11_TEST_FILES ${PYBIND11_CPPTEST_FILES})
list(SORT PYBIND11_PYTEST_FILES)
# Contains the set of test files that require pybind11_cross_module_tests to be # Contains the set of test files that require pybind11_cross_module_tests to be
# built; if none of these are built (i.e. because TEST_OVERRIDE is used and # built; if none of these are built (i.e. because TEST_OVERRIDE is used and
# doesn't include them) the second module doesn't get built. # doesn't include them) the second module doesn't get built.
set(PYBIND11_CROSS_MODULE_TESTS test_exceptions.py test_local_bindings.py test_stl.py tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_stl_binders.py"
test_stl_binders.py) "pybind11_cross_module_tests")
set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py) # And add additional targets for other tests.
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
set(PYBIND11_EIGEN_REPO
"https://gitlab.com/libeigen/eigen.git"
CACHE STRING "Eigen repository to use for tests")
# Always use a hash for reconfigure speed and security reasons
# Include the version number for pretty printing (keep in sync)
set(PYBIND11_EIGEN_VERSION_AND_HASH
"3.4.0;929bc0e191d0927b1735b9a1ddc0e8b77e3a25ec"
CACHE STRING "Eigen version to use for tests, format: VERSION;HASH")
list(GET PYBIND11_EIGEN_VERSION_AND_HASH 0 PYBIND11_EIGEN_VERSION_STRING)
list(GET PYBIND11_EIGEN_VERSION_AND_HASH 1 PYBIND11_EIGEN_VERSION_HASH)
# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but # Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but
# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed" # keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed"
@ -182,22 +247,26 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN") message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN")
endif() endif()
set(EIGEN3_VERSION_STRING "3.3.8")
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
eigen eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
GIT_TAG ${EIGEN3_VERSION_STRING}) GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}")
FetchContent_GetProperties(eigen) FetchContent_GetProperties(eigen)
if(NOT eigen_POPULATED) if(NOT eigen_POPULATED)
message(STATUS "Downloading Eigen") message(
STATUS
"Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}"
)
FetchContent_Populate(eigen) FetchContent_Populate(eigen)
endif() endif()
set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR})
set(EIGEN3_FOUND TRUE) set(EIGEN3_FOUND TRUE)
# When getting locally, the version is not visible from a superprojet,
# so just force it.
set(EIGEN3_VERSION "${PYBIND11_EIGEN_VERSION_STRING}")
else() else()
find_package(Eigen3 3.2.7 QUIET CONFIG) find_package(Eigen3 3.2.7 QUIET CONFIG)
@ -248,7 +317,9 @@ if(Boost_FOUND)
endif() endif()
# Check if we need to add -lstdc++fs or -lc++fs or nothing # Check if we need to add -lstdc++fs or -lc++fs or nothing
if(MSVC) if(DEFINED CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD LESS 17)
set(STD_FS_NO_LIB_NEEDED TRUE)
elseif(MSVC)
set(STD_FS_NO_LIB_NEEDED TRUE) set(STD_FS_NO_LIB_NEEDED TRUE)
else() else()
file( file(
@ -278,7 +349,7 @@ elseif(${STD_FS_NEEDS_CXXFS})
elseif(${STD_FS_NO_LIB_NEEDED}) elseif(${STD_FS_NO_LIB_NEEDED})
set(STD_FS_LIB "") set(STD_FS_LIB "")
else() else()
message(WARNING "Unknown compiler - not passing -lstdc++fs") message(WARNING "Unknown C++17 compiler - not passing -lstdc++fs")
set(STD_FS_LIB "") set(STD_FS_LIB "")
endif() endif()
@ -306,6 +377,9 @@ function(pybind11_enable_warnings target_name)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang|IntelLLVM)") elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang|IntelLLVM)")
target_compile_options(${target_name} PRIVATE -Werror) target_compile_options(${target_name} PRIVATE -Werror)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
if(CMAKE_CXX_STANDARD EQUAL 17) # See PR #3570
target_compile_options(${target_name} PRIVATE -Wno-conversion)
endif()
target_compile_options( target_compile_options(
${target_name} ${target_name}
PRIVATE PRIVATE
@ -329,21 +403,17 @@ endfunction()
set(test_targets pybind11_tests) set(test_targets pybind11_tests)
# Build pybind11_cross_module_tests if any test_whatever.py are being built that require it # Check if any tests need extra targets by iterating through the mappings registered.
foreach(t ${PYBIND11_CROSS_MODULE_TESTS}) foreach(i ${PYBIND11_TEST_EXTRA_TARGETS})
list(FIND PYBIND11_PYTEST_FILES ${t} i) foreach(needle ${PYBIND11_TEST_EXTRA_TARGETS_NEEDLES_${i}})
if(i GREATER -1) if(needle IN_LIST PYBIND11_PYTEST_FILES)
list(APPEND test_targets pybind11_cross_module_tests) # Add all the additional targets to the test list. List join in newer cmake.
break() foreach(extra_target ${PYBIND11_TEST_EXTRA_TARGETS_ADDITION_${i}})
endif() list(APPEND test_targets ${extra_target})
endforeach() endforeach()
break() # Breaks out of the needle search, continues with the next mapping.
foreach(t ${PYBIND11_CROSS_MODULE_GIL_TESTS}) endif()
list(FIND PYBIND11_PYTEST_FILES ${t} i) endforeach()
if(i GREATER -1)
list(APPEND test_targets cross_module_gil_utils)
break()
endif()
endforeach() endforeach()
# Support CUDA testing by forcing the target file to compile with NVCC # Support CUDA testing by forcing the target file to compile with NVCC
@ -409,6 +479,14 @@ foreach(target ${test_targets})
endif() endif()
endforeach() endforeach()
# Provide nice organisation in IDEs
if(NOT CMAKE_VERSION VERSION_LESS 3.8)
source_group(
TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include"
PREFIX "Header Files"
FILES ${PYBIND11_HEADERS})
endif()
# Make sure pytest is found or produce a warning # Make sure pytest is found or produce a warning
pybind11_find_import(pytest VERSION 3.1) pybind11_find_import(pytest VERSION 3.1)

View File

@ -56,7 +56,8 @@ from the ConstructorStats instance `.values()` method.
In some cases, when you need to track instances of a C++ class not registered with pybind11, you In some cases, when you need to track instances of a C++ class not registered with pybind11, you
need to add a function returning the ConstructorStats for the C++ class; this can be done with: need to add a function returning the ConstructorStats for the C++ class; this can be done with:
m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>, py::return_value_policy::reference) m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>,
py::return_value_policy::reference)
Finally, you can suppress the output messages, but keep the constructor tracking (for Finally, you can suppress the output messages, but keep the constructor tracking (for
inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g. inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g.
@ -65,15 +66,18 @@ inspection/testing in python) by using the functions with `print_` replaced with
*/ */
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <unordered_map>
#include <list> #include <list>
#include <typeindex>
#include <sstream> #include <sstream>
#include <typeindex>
#include <unordered_map>
class ConstructorStats { class ConstructorStats {
protected: protected:
std::unordered_map<void*, int> _instances; // Need a map rather than set because members can shared address with parents std::unordered_map<void *, int> _instances; // Need a map rather than set because members can
std::list<std::string> _values; // Used to track values (e.g. of value constructors) // shared address with parents
std::list<std::string> _values; // Used to track values
// (e.g. of value constructors)
public: public:
int default_constructions = 0; int default_constructions = 0;
int copy_constructions = 0; int copy_constructions = 0;
@ -96,26 +100,26 @@ public:
default_constructions++; default_constructions++;
} }
void created(void *inst) { void created(void *inst) { ++_instances[inst]; }
++_instances[inst];
}
void destroyed(void *inst) { void destroyed(void *inst) {
if (--_instances[inst] < 0) if (--_instances[inst] < 0) {
throw std::runtime_error("cstats.destroyed() called with unknown " throw std::runtime_error("cstats.destroyed() called with unknown "
"instance; potential double-destruction " "instance; potential double-destruction "
"or a missing cstats.created()"); "or a missing cstats.created()");
}
} }
static void gc() { static void gc() {
// Force garbage collection to ensure any pending destructors are invoked: // Force garbage collection to ensure any pending destructors are invoked:
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
PyObject *globals = PyEval_GetGlobals(); PyObject *globals = PyEval_GetGlobals();
PyObject *result = PyRun_String( PyObject *result = PyRun_String("import gc\n"
"import gc\n" "for i in range(2):"
"for i in range(2):" " gc.collect()\n",
" gc.collect()\n", Py_file_input,
Py_file_input, globals, globals); globals,
globals);
if (result == nullptr) if (result == nullptr)
throw py::error_already_set(); throw py::error_already_set();
Py_DECREF(result); Py_DECREF(result);
@ -127,15 +131,18 @@ public:
int alive() { int alive() {
gc(); gc();
int total = 0; int total = 0;
for (const auto &p : _instances) for (const auto &p : _instances) {
if (p.second > 0) if (p.second > 0) {
total += p.second; total += p.second;
}
}
return total; return total;
} }
void value() {} // Recursion terminator void value() {} // Recursion terminator
// Takes one or more values, converts them to strings, then stores them. // Takes one or more values, converts them to strings, then stores them.
template <typename T, typename... Tmore> void value(const T &v, Tmore &&...args) { template <typename T, typename... Tmore>
void value(const T &v, Tmore &&...args) {
std::ostringstream oss; std::ostringstream oss;
oss << v; oss << v;
_values.push_back(oss.str()); _values.push_back(oss.str());
@ -145,19 +152,22 @@ public:
// Move out stored values // Move out stored values
py::list values() { py::list values() {
py::list l; py::list l;
for (const auto &v : _values) l.append(py::cast(v)); for (const auto &v : _values) {
l.append(py::cast(v));
}
_values.clear(); _values.clear();
return l; return l;
} }
// Gets constructor stats from a C++ type index // Gets constructor stats from a C++ type index
static ConstructorStats& get(std::type_index type) { static ConstructorStats &get(std::type_index type) {
static std::unordered_map<std::type_index, ConstructorStats> all_cstats; static std::unordered_map<std::type_index, ConstructorStats> all_cstats;
return all_cstats[type]; return all_cstats[type];
} }
// Gets constructor stats from a C++ type // Gets constructor stats from a C++ type
template <typename T> static ConstructorStats& get() { template <typename T>
static ConstructorStats &get() {
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
gc(); gc();
#endif #endif
@ -165,11 +175,12 @@ public:
} }
// Gets constructor stats from a Python class // Gets constructor stats from a Python class
static ConstructorStats& get(py::object class_) { static ConstructorStats &get(py::object class_) {
auto &internals = py::detail::get_internals(); auto &internals = py::detail::get_internals();
const std::type_index *t1 = nullptr, *t2 = nullptr; const std::type_index *t1 = nullptr, *t2 = nullptr;
try { try {
auto *type_info = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0); auto *type_info
= internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0);
for (auto &p : internals.registered_types_cpp) { for (auto &p : internals.registered_types_cpp) {
if (p.second == type_info) { if (p.second == type_info) {
if (t1) { if (t1) {
@ -179,17 +190,23 @@ public:
t1 = &p.first; t1 = &p.first;
} }
} }
} catch (const std::out_of_range &) {
}
if (!t1) {
throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
} }
catch (const std::out_of_range&) {}
if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
auto &cs1 = get(*t1); auto &cs1 = get(*t1);
// If we have both a t1 and t2 match, one is probably the trampoline class; return whichever // If we have both a t1 and t2 match, one is probably the trampoline class; return
// has more constructions (typically one or the other will be 0) // whichever has more constructions (typically one or the other will be 0)
if (t2) { if (t2) {
auto &cs2 = get(*t2); auto &cs2 = get(*t2);
int cs1_total = cs1.default_constructions + cs1.copy_constructions + cs1.move_constructions + (int) cs1._values.size(); int cs1_total = cs1.default_constructions + cs1.copy_constructions
int cs2_total = cs2.default_constructions + cs2.copy_constructions + cs2.move_constructions + (int) cs2._values.size(); + cs1.move_constructions + (int) cs1._values.size();
if (cs2_total > cs1_total) return cs2; int cs2_total = cs2.default_constructions + cs2.copy_constructions
+ cs2.move_constructions + (int) cs2._values.size();
if (cs2_total > cs1_total) {
return cs2;
}
} }
return cs1; return cs1;
} }
@ -198,78 +215,108 @@ public:
// To track construction/destruction, you need to call these methods from the various // To track construction/destruction, you need to call these methods from the various
// constructors/operators. The ones that take extra values record the given values in the // constructors/operators. The ones that take extra values record the given values in the
// constructor stats values for later inspection. // constructor stats values for later inspection.
template <class T> void track_copy_created(T *inst) { ConstructorStats::get<T>().copy_created(inst); } template <class T>
template <class T> void track_move_created(T *inst) { ConstructorStats::get<T>().move_created(inst); } void track_copy_created(T *inst) {
template <class T, typename... Values> void track_copy_assigned(T *, Values &&...values) { ConstructorStats::get<T>().copy_created(inst);
}
template <class T>
void track_move_created(T *inst) {
ConstructorStats::get<T>().move_created(inst);
}
template <class T, typename... Values>
void track_copy_assigned(T *, Values &&...values) {
auto &cst = ConstructorStats::get<T>(); auto &cst = ConstructorStats::get<T>();
cst.copy_assignments++; cst.copy_assignments++;
cst.value(std::forward<Values>(values)...); cst.value(std::forward<Values>(values)...);
} }
template <class T, typename... Values> void track_move_assigned(T *, Values &&...values) { template <class T, typename... Values>
void track_move_assigned(T *, Values &&...values) {
auto &cst = ConstructorStats::get<T>(); auto &cst = ConstructorStats::get<T>();
cst.move_assignments++; cst.move_assignments++;
cst.value(std::forward<Values>(values)...); cst.value(std::forward<Values>(values)...);
} }
template <class T, typename... Values> void track_default_created(T *inst, Values &&...values) { template <class T, typename... Values>
void track_default_created(T *inst, Values &&...values) {
auto &cst = ConstructorStats::get<T>(); auto &cst = ConstructorStats::get<T>();
cst.default_created(inst); cst.default_created(inst);
cst.value(std::forward<Values>(values)...); cst.value(std::forward<Values>(values)...);
} }
template <class T, typename... Values> void track_created(T *inst, Values &&...values) { template <class T, typename... Values>
void track_created(T *inst, Values &&...values) {
auto &cst = ConstructorStats::get<T>(); auto &cst = ConstructorStats::get<T>();
cst.created(inst); cst.created(inst);
cst.value(std::forward<Values>(values)...); cst.value(std::forward<Values>(values)...);
} }
template <class T, typename... Values> void track_destroyed(T *inst) { template <class T, typename... Values>
void track_destroyed(T *inst) {
ConstructorStats::get<T>().destroyed(inst); ConstructorStats::get<T>().destroyed(inst);
} }
template <class T, typename... Values> void track_values(T *, Values &&...values) { template <class T, typename... Values>
void track_values(T *, Values &&...values) {
ConstructorStats::get<T>().value(std::forward<Values>(values)...); ConstructorStats::get<T>().value(std::forward<Values>(values)...);
} }
/// Don't cast pointers to Python, print them as strings /// Don't cast pointers to Python, print them as strings
inline const char *format_ptrs(const char *p) { return p; } inline const char *format_ptrs(const char *p) { return p; }
template <typename T> template <typename T>
py::str format_ptrs(T *p) { return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p)); } py::str format_ptrs(T *p) {
return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p));
}
template <typename T> template <typename T>
auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) { return std::forward<T>(x); } auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) {
return std::forward<T>(x);
}
template <class T, typename... Output> template <class T, typename... Output>
void print_constr_details(T *inst, const std::string &action, Output &&...output) { void print_constr_details(T *inst, const std::string &action, Output &&...output) {
py::print("###", py::type_id<T>(), "@", format_ptrs(inst), action, py::print("###",
py::type_id<T>(),
"@",
format_ptrs(inst),
action,
format_ptrs(std::forward<Output>(output))...); format_ptrs(std::forward<Output>(output))...);
} }
// Verbose versions of the above: // Verbose versions of the above:
template <class T, typename... Values> void print_copy_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values template <class T, typename... Values>
void print_copy_created(T *inst,
Values &&...values) { // NB: this prints, but doesn't store, given values
print_constr_details(inst, "created via copy constructor", values...); print_constr_details(inst, "created via copy constructor", values...);
track_copy_created(inst); track_copy_created(inst);
} }
template <class T, typename... Values> void print_move_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values template <class T, typename... Values>
void print_move_created(T *inst,
Values &&...values) { // NB: this prints, but doesn't store, given values
print_constr_details(inst, "created via move constructor", values...); print_constr_details(inst, "created via move constructor", values...);
track_move_created(inst); track_move_created(inst);
} }
template <class T, typename... Values> void print_copy_assigned(T *inst, Values &&...values) { template <class T, typename... Values>
void print_copy_assigned(T *inst, Values &&...values) {
print_constr_details(inst, "assigned via copy assignment", values...); print_constr_details(inst, "assigned via copy assignment", values...);
track_copy_assigned(inst, values...); track_copy_assigned(inst, values...);
} }
template <class T, typename... Values> void print_move_assigned(T *inst, Values &&...values) { template <class T, typename... Values>
void print_move_assigned(T *inst, Values &&...values) {
print_constr_details(inst, "assigned via move assignment", values...); print_constr_details(inst, "assigned via move assignment", values...);
track_move_assigned(inst, values...); track_move_assigned(inst, values...);
} }
template <class T, typename... Values> void print_default_created(T *inst, Values &&...values) { template <class T, typename... Values>
void print_default_created(T *inst, Values &&...values) {
print_constr_details(inst, "created via default constructor", values...); print_constr_details(inst, "created via default constructor", values...);
track_default_created(inst, values...); track_default_created(inst, values...);
} }
template <class T, typename... Values> void print_created(T *inst, Values &&...values) { template <class T, typename... Values>
void print_created(T *inst, Values &&...values) {
print_constr_details(inst, "created", values...); print_constr_details(inst, "created", values...);
track_created(inst, values...); track_created(inst, values...);
} }
template <class T, typename... Values> void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values template <class T, typename... Values>
void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values
print_constr_details(inst, "destroyed", values...); print_constr_details(inst, "destroyed", values...);
track_destroyed(inst); track_destroyed(inst);
} }
template <class T, typename... Values> void print_values(T *inst, Values &&...values) { template <class T, typename... Values>
void print_values(T *inst, Values &&...values) {
print_constr_details(inst, ":", values...); print_constr_details(inst, ":", values...);
track_values(inst, values...); track_values(inst, values...);
} }

View File

@ -7,6 +7,7 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <cstdint> #include <cstdint>
// This file mimics a DSO that makes pybind11 calls but does not define a // This file mimics a DSO that makes pybind11 calls but does not define a
@ -25,34 +26,25 @@ void gil_acquire() { py::gil_scoped_acquire gil; }
constexpr char kModuleName[] = "cross_module_gil_utils"; constexpr char kModuleName[] = "cross_module_gil_utils";
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
struct PyModuleDef moduledef = { struct PyModuleDef moduledef
PyModuleDef_HEAD_INIT, = {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL};
kModuleName,
NULL,
0,
NULL,
NULL,
NULL,
NULL,
NULL
};
#else #else
PyMethodDef module_methods[] = { PyMethodDef module_methods[] = {{NULL, NULL, 0, NULL}};
{NULL, NULL, 0, NULL}
};
#endif #endif
} // namespace } // namespace
extern "C" PYBIND11_EXPORT extern "C" PYBIND11_EXPORT
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
PyObject* PyInit_cross_module_gil_utils() PyObject *
PyInit_cross_module_gil_utils()
#else #else
void initcross_module_gil_utils() void
initcross_module_gil_utils()
#endif #endif
{ {
PyObject* m = PyObject *m =
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
PyModule_Create(&moduledef); PyModule_Create(&moduledef);
#else #else
@ -60,11 +52,10 @@ void initcross_module_gil_utils()
#endif #endif
if (m != NULL) { if (m != NULL) {
static_assert( static_assert(sizeof(&gil_acquire) == sizeof(void *),
sizeof(&gil_acquire) == sizeof(void*), "Function pointer must have the same size as void*");
"Function pointer must have the same size as void*"); PyModule_AddObject(
PyModule_AddObject(m, "gil_acquire_funcaddr", m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire)));
PyLong_FromVoidPtr(reinterpret_cast<void*>(&gil_acquire)));
} }
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3

View File

@ -1,13 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import sys
import subprocess import subprocess
import sys
from textwrap import dedent from textwrap import dedent
import pytest import pytest
DIR = os.path.abspath(os.path.dirname(__file__)) DIR = os.path.abspath(os.path.dirname(__file__))
MAIN_DIR = os.path.dirname(os.path.dirname(DIR)) MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
@pytest.mark.parametrize("parallel", [False, True]) @pytest.mark.parametrize("parallel", [False, True])
@ -71,13 +72,20 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
encoding="ascii", encoding="ascii",
) )
subprocess.check_call( out = subprocess.check_output(
[sys.executable, "setup.py", "build_ext", "--inplace"], [sys.executable, "setup.py", "build_ext", "--inplace"],
stdout=sys.stdout,
stderr=sys.stderr,
) )
if not WIN:
assert b"-g0" in out
out = subprocess.check_output(
[sys.executable, "setup.py", "build_ext", "--inplace", "--force"],
env=dict(os.environ, CFLAGS="-g"),
)
if not WIN:
assert b"-g0" not in out
# Debug helper printout, normally hidden # Debug helper printout, normally hidden
print(out)
for item in tmpdir.listdir(): for item in tmpdir.listdir():
print(item.basename) print(item.basename)

View File

@ -1,12 +1,13 @@
#pragma once #pragma once
#include <utility>
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <utility>
/// Simple class used to test py::local: /// Simple class used to test py::local:
template <int> class LocalBase { template <int>
class LocalBase {
public: public:
LocalBase(int i) : i(i) { } explicit LocalBase(int i) : i(i) {}
int i = -1; int i = -1;
}; };
@ -35,32 +36,57 @@ using NonLocalVec2 = std::vector<NonLocal2>;
using NonLocalMap = std::unordered_map<std::string, NonLocalType>; using NonLocalMap = std::unordered_map<std::string, NonLocalType>;
using NonLocalMap2 = std::unordered_map<std::string, uint8_t>; using NonLocalMap2 = std::unordered_map<std::string, uint8_t>;
// Exception that will be caught via the module local translator.
class LocalException : public std::exception {
public:
explicit LocalException(const char *m) : message{m} {}
const char *what() const noexcept override { return message.c_str(); }
private:
std::string message = "";
};
// Exception that will be registered with register_local_exception_translator
class LocalSimpleException : public std::exception {
public:
explicit LocalSimpleException(const char *m) : message{m} {}
const char *what() const noexcept override { return message.c_str(); }
private:
std::string message = "";
};
PYBIND11_MAKE_OPAQUE(LocalVec); PYBIND11_MAKE_OPAQUE(LocalVec);
PYBIND11_MAKE_OPAQUE(LocalVec2); PYBIND11_MAKE_OPAQUE(LocalVec2);
PYBIND11_MAKE_OPAQUE(LocalMap); PYBIND11_MAKE_OPAQUE(LocalMap);
PYBIND11_MAKE_OPAQUE(NonLocalVec); PYBIND11_MAKE_OPAQUE(NonLocalVec);
//PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2 // PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2
PYBIND11_MAKE_OPAQUE(NonLocalMap); PYBIND11_MAKE_OPAQUE(NonLocalMap);
PYBIND11_MAKE_OPAQUE(NonLocalMap2); PYBIND11_MAKE_OPAQUE(NonLocalMap2);
// Simple bindings (used with the above): // Simple bindings (used with the above):
template <typename T, int Adjust = 0, typename... Args> template <typename T, int Adjust = 0, typename... Args>
py::class_<T> bind_local(Args && ...args) { py::class_<T> bind_local(Args &&...args) {
return py::class_<T>(std::forward<Args>(args)...) return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) {
.def(py::init<int>()) return i.i + Adjust;
.def("get", [](T &i) { return i.i + Adjust; }); });
}; };
// Simulate a foreign library base class (to match the example in the docs): // Simulate a foreign library base class (to match the example in the docs):
namespace pets { namespace pets {
class Pet { class Pet {
public: public:
Pet(std::string name) : name_(std::move(name)) {} explicit Pet(std::string name) : name_(std::move(name)) {}
std::string name_; std::string name_;
const std::string &name() const { return name_; } const std::string &name() const { return name_; }
}; };
} // namespace pets } // namespace pets
struct MixGL { int i; MixGL(int i) : i{i} {} }; struct MixGL {
struct MixGL2 { int i; MixGL2(int i) : i{i} {} }; int i;
explicit MixGL(int i) : i{i} {}
};
struct MixGL2 {
int i;
explicit MixGL2(int i) : i{i} {}
};

View File

@ -1,8 +1,9 @@
#if !defined(__OBJECT_H) #if !defined(__OBJECT_H)
#define __OBJECT_H # define __OBJECT_H
#include <atomic> # include "constructor_stats.h"
#include "constructor_stats.h"
# include <atomic>
/// Reference counted object base class /// Reference counted object base class
class Object { class Object {
@ -27,20 +28,23 @@ public:
*/ */
void decRef(bool dealloc = true) const { void decRef(bool dealloc = true) const {
--m_refCount; --m_refCount;
if (m_refCount == 0 && dealloc) if (m_refCount == 0 && dealloc) {
delete this; delete this;
else if (m_refCount < 0) } else if (m_refCount < 0) {
throw std::runtime_error("Internal error: reference count < 0!"); throw std::runtime_error("Internal error: reference count < 0!");
}
} }
virtual std::string toString() const = 0; virtual std::string toString() const = 0;
protected: protected:
/** \brief Virtual protected deconstructor. /** \brief Virtual protected deconstructor.
* (Will only be called by \ref ref) * (Will only be called by \ref ref)
*/ */
virtual ~Object() { print_destroyed(this); } virtual ~Object() { print_destroyed(this); }
private: private:
mutable std::atomic<int> m_refCount { 0 }; mutable std::atomic<int> m_refCount{0};
}; };
// Tag class used to track constructions of ref objects. When we track constructors, below, we // Tag class used to track constructions of ref objects. When we track constructors, below, we
@ -59,80 +63,105 @@ class ref_tag {};
* *
* \ingroup libcore * \ingroup libcore
*/ */
template <typename T> class ref { template <typename T>
class ref {
public: public:
/// Create a nullptr reference /// Create a nullptr reference
ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); } ref() : m_ptr(nullptr) {
print_default_created(this);
track_default_created((ref_tag *) this);
}
/// Construct a reference from a pointer /// Construct a reference from a pointer
ref(T *ptr) : m_ptr(ptr) { explicit ref(T *ptr) : m_ptr(ptr) {
if (m_ptr) ((Object *) m_ptr)->incRef(); if (m_ptr) {
((Object *) m_ptr)->incRef();
print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer"); }
print_created(this, "from pointer", m_ptr);
track_created((ref_tag *) this, "from pointer");
} }
/// Copy constructor /// Copy constructor
ref(const ref &r) : m_ptr(r.m_ptr) { ref(const ref &r) : m_ptr(r.m_ptr) {
if (m_ptr) if (m_ptr) {
((Object *) m_ptr)->incRef(); ((Object *) m_ptr)->incRef();
}
print_copy_created(this, "with pointer", m_ptr); track_copy_created((ref_tag*) this); print_copy_created(this, "with pointer", m_ptr);
track_copy_created((ref_tag *) this);
} }
/// Move constructor /// Move constructor
ref(ref &&r) noexcept : m_ptr(r.m_ptr) { ref(ref &&r) noexcept : m_ptr(r.m_ptr) {
r.m_ptr = nullptr; r.m_ptr = nullptr;
print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this); print_move_created(this, "with pointer", m_ptr);
track_move_created((ref_tag *) this);
} }
/// Destroy this reference /// Destroy this reference
~ref() { ~ref() {
if (m_ptr) if (m_ptr) {
((Object *) m_ptr)->decRef(); ((Object *) m_ptr)->decRef();
}
print_destroyed(this); track_destroyed((ref_tag*) this); print_destroyed(this);
track_destroyed((ref_tag *) this);
} }
/// Move another reference into the current one /// Move another reference into the current one
ref &operator=(ref &&r) noexcept { ref &operator=(ref &&r) noexcept {
print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this); print_move_assigned(this, "pointer", r.m_ptr);
track_move_assigned((ref_tag *) this);
if (*this == r) if (*this == r) {
return *this; return *this;
if (m_ptr) }
if (m_ptr) {
((Object *) m_ptr)->decRef(); ((Object *) m_ptr)->decRef();
}
m_ptr = r.m_ptr; m_ptr = r.m_ptr;
r.m_ptr = nullptr; r.m_ptr = nullptr;
return *this; return *this;
} }
/// Overwrite this reference with another reference /// Overwrite this reference with another reference
ref& operator=(const ref& r) { ref &operator=(const ref &r) {
print_copy_assigned(this, "pointer", r.m_ptr); track_copy_assigned((ref_tag*) this); if (this == &r) {
if (m_ptr == r.m_ptr)
return *this; return *this;
if (m_ptr) }
print_copy_assigned(this, "pointer", r.m_ptr);
track_copy_assigned((ref_tag *) this);
if (m_ptr == r.m_ptr) {
return *this;
}
if (m_ptr) {
((Object *) m_ptr)->decRef(); ((Object *) m_ptr)->decRef();
}
m_ptr = r.m_ptr; m_ptr = r.m_ptr;
if (m_ptr) if (m_ptr) {
((Object *) m_ptr)->incRef(); ((Object *) m_ptr)->incRef();
}
return *this; return *this;
} }
/// Overwrite this reference with a pointer to another object /// Overwrite this reference with a pointer to another object
ref& operator=(T *ptr) { ref &operator=(T *ptr) {
print_values(this, "assigned pointer"); track_values((ref_tag*) this, "assigned pointer"); print_values(this, "assigned pointer");
track_values((ref_tag *) this, "assigned pointer");
if (m_ptr == ptr) if (m_ptr == ptr) {
return *this; return *this;
if (m_ptr) }
if (m_ptr) {
((Object *) m_ptr)->decRef(); ((Object *) m_ptr)->decRef();
}
m_ptr = ptr; m_ptr = ptr;
if (m_ptr) if (m_ptr) {
((Object *) m_ptr)->incRef(); ((Object *) m_ptr)->incRef();
}
return *this; return *this;
} }
@ -143,31 +172,32 @@ public:
bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; } bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; }
/// Compare this reference with a pointer /// Compare this reference with a pointer
bool operator==(const T* ptr) const { return m_ptr == ptr; } bool operator==(const T *ptr) const { return m_ptr == ptr; }
/// Compare this reference with a pointer /// Compare this reference with a pointer
bool operator!=(const T* ptr) const { return m_ptr != ptr; } bool operator!=(const T *ptr) const { return m_ptr != ptr; }
/// Access the object referenced by this reference /// Access the object referenced by this reference
T* operator->() { return m_ptr; } T *operator->() { return m_ptr; }
/// Access the object referenced by this reference /// Access the object referenced by this reference
const T* operator->() const { return m_ptr; } const T *operator->() const { return m_ptr; }
/// Return a C++ reference to the referenced object /// Return a C++ reference to the referenced object
T& operator*() { return *m_ptr; } T &operator*() { return *m_ptr; }
/// Return a const C++ reference to the referenced object /// Return a const C++ reference to the referenced object
const T& operator*() const { return *m_ptr; } const T &operator*() const { return *m_ptr; }
/// Return a pointer to the referenced object /// Return a pointer to the referenced object
operator T* () { return m_ptr; } explicit operator T *() { return m_ptr; }
/// Return a const pointer to the referenced object /// Return a const pointer to the referenced object
T* get_ptr() { return m_ptr; } T *get_ptr() { return m_ptr; }
/// Return a pointer to the referenced object /// Return a pointer to the referenced object
const T* get_ptr() const { return m_ptr; } const T *get_ptr() const { return m_ptr; }
private: private:
T *m_ptr; T *m_ptr;
}; };

View File

@ -7,12 +7,12 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "local_bindings.h"
#include "test_exceptions.h"
#include <pybind11/stl_bind.h> #include <pybind11/stl_bind.h>
#include "local_bindings.h"
#include "pybind11_tests.h"
#include "test_exceptions.h"
#include <numeric> #include <numeric>
#include <utility> #include <utility>
@ -29,24 +29,46 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
bind_local<ExternalType2>(m, "ExternalType2", py::module_local()); bind_local<ExternalType2>(m, "ExternalType2", py::module_local());
// test_exceptions.py // test_exceptions.py
m.def("raise_runtime_error", []() { PyErr_SetString(PyExc_RuntimeError, "My runtime error"); throw py::error_already_set(); }); py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException");
m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); }); m.def("raise_runtime_error", []() {
PyErr_SetString(PyExc_RuntimeError, "My runtime error");
throw py::error_already_set();
});
m.def("raise_value_error", []() {
PyErr_SetString(PyExc_ValueError, "My value error");
throw py::error_already_set();
});
m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); }); m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); });
m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); }); m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); });
m.def("throw_stop_iteration", []() { throw py::stop_iteration(); }); m.def("throw_stop_iteration", []() { throw py::stop_iteration(); });
m.def("throw_local_error", []() { throw LocalException("just local"); });
m.def("throw_local_simple_error", []() { throw LocalSimpleException("external mod"); });
py::register_exception_translator([](std::exception_ptr p) { py::register_exception_translator([](std::exception_ptr p) {
try { try {
if (p) std::rethrow_exception(p); if (p) {
} catch (const shared_exception &e) { std::rethrow_exception(p);
PyErr_SetString(PyExc_KeyError, e.what()); }
} } catch (const shared_exception &e) {
PyErr_SetString(PyExc_KeyError, e.what());
}
});
// translate the local exception into a key error but only in this module
py::register_local_exception_translator([](std::exception_ptr p) {
try {
if (p) {
std::rethrow_exception(p);
}
} catch (const LocalException &e) {
PyErr_SetString(PyExc_KeyError, e.what());
}
}); });
// test_local_bindings.py // test_local_bindings.py
// Local to both: // Local to both:
bind_local<LocalType, 1>(m, "LocalType", py::module_local()) bind_local<LocalType, 1>(m, "LocalType", py::module_local()).def("get2", [](LocalType &t) {
.def("get2", [](LocalType &t) { return t.i + 2; }) return t.i + 2;
; });
// Can only be called with our python type: // Can only be called with our python type:
m.def("local_value", [](LocalType &l) { return l.i; }); m.def("local_value", [](LocalType &l) { return l.i; });
@ -54,9 +76,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
// test_nonlocal_failure // test_nonlocal_failure
// This registration will fail (global registration when LocalFail is already registered // This registration will fail (global registration when LocalFail is already registered
// globally in the main test module): // globally in the main test module):
m.def("register_nonlocal", [m]() { m.def("register_nonlocal", [m]() { bind_local<NonLocalType, 0>(m, "NonLocalType"); });
bind_local<NonLocalType, 0>(m, "NonLocalType");
});
// test_stl_bind_local // test_stl_bind_local
// stl_bind.h binders defaults to py::module_local if the types are local or converting: // stl_bind.h binders defaults to py::module_local if the types are local or converting:
@ -66,27 +86,21 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
// test_stl_bind_global // test_stl_bind_global
// and global if the type (or one of the types, for the map) is global (so these will fail, // and global if the type (or one of the types, for the map) is global (so these will fail,
// assuming pybind11_tests is already loaded): // assuming pybind11_tests is already loaded):
m.def("register_nonlocal_vec", [m]() { m.def("register_nonlocal_vec", [m]() { py::bind_vector<NonLocalVec>(m, "NonLocalVec"); });
py::bind_vector<NonLocalVec>(m, "NonLocalVec"); m.def("register_nonlocal_map", [m]() { py::bind_map<NonLocalMap>(m, "NonLocalMap"); });
});
m.def("register_nonlocal_map", [m]() {
py::bind_map<NonLocalMap>(m, "NonLocalMap");
});
// The default can, however, be overridden to global using `py::module_local()` or // The default can, however, be overridden to global using `py::module_local()` or
// `py::module_local(false)`. // `py::module_local(false)`.
// Explicitly made local: // Explicitly made local:
py::bind_vector<NonLocalVec2>(m, "NonLocalVec2", py::module_local()); py::bind_vector<NonLocalVec2>(m, "NonLocalVec2", py::module_local());
// Explicitly made global (and so will fail to bind): // Explicitly made global (and so will fail to bind):
m.def("register_nonlocal_map2", [m]() { m.def("register_nonlocal_map2",
py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false)); [m]() { py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false)); });
});
// test_mixed_local_global // test_mixed_local_global
// We try this both with the global type registered first and vice versa (the order shouldn't // We try this both with the global type registered first and vice versa (the order shouldn't
// matter). // matter).
m.def("register_mixed_global_local", [m]() { m.def("register_mixed_global_local",
bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local()); [m]() { bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local()); });
});
m.def("register_mixed_local_global", [m]() { m.def("register_mixed_local_global", [m]() {
bind_local<MixedLocalGlobal, 2000>(m, "MixedLocalGlobal", py::module_local(false)); bind_local<MixedLocalGlobal, 2000>(m, "MixedLocalGlobal", py::module_local(false));
}); });
@ -94,14 +108,14 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); }); m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); });
// test_internal_locals_differ // test_internal_locals_differ
m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::registered_local_types_cpp(); }); m.def("local_cpp_types_addr",
[]() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; });
// test_stl_caster_vs_stl_bind // test_stl_caster_vs_stl_bind
py::bind_vector<std::vector<int>>(m, "VectorInt"); py::bind_vector<std::vector<int>>(m, "VectorInt");
m.def("load_vector_via_binding", [](std::vector<int> &v) { m.def("load_vector_via_binding",
return std::accumulate(v.begin(), v.end(), 0); [](std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); });
});
// test_cross_module_calls // test_cross_module_calls
m.def("return_self", [](LocalVec *v) { return v; }); m.def("return_self", [](LocalVec *v) { return v; });
@ -109,13 +123,11 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
class Dog : public pets::Pet { class Dog : public pets::Pet {
public: public:
Dog(std::string name) : Pet(std::move(name)) {} explicit Dog(std::string name) : Pet(std::move(name)) {}
}; };
py::class_<pets::Pet>(m, "Pet", py::module_local()) py::class_<pets::Pet>(m, "Pet", py::module_local()).def("name", &pets::Pet::name);
.def("name", &pets::Pet::name);
// Binding for local extending class: // Binding for local extending class:
py::class_<Dog, pets::Pet>(m, "Dog") py::class_<Dog, pets::Pet>(m, "Dog").def(py::init<std::string>());
.def(py::init<std::string>());
m.def("pet_name", [](pets::Pet &p) { return p.name(); }); m.def("pet_name", [](pets::Pet &p) { return p.name(); });
py::class_<MixGL>(m, "MixGL", py::module_local()).def(py::init<int>()); py::class_<MixGL>(m, "MixGL", py::module_local()).def(py::init<int>());

View File

@ -8,6 +8,7 @@
*/ */
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
#include <functional> #include <functional>
@ -31,9 +32,7 @@ std::list<std::function<void(py::module_ &)>> &initializers() {
return inits; return inits;
} }
test_initializer::test_initializer(Initializer init) { test_initializer::test_initializer(Initializer init) { initializers().emplace_back(init); }
initializers().emplace_back(init);
}
test_initializer::test_initializer(const char *submodule_name, Initializer init) { test_initializer::test_initializer(const char *submodule_name, Initializer init) {
initializers().emplace_back([=](py::module_ &parent) { initializers().emplace_back([=](py::module_ &parent) {
@ -51,15 +50,16 @@ void bind_ConstructorStats(py::module_ &m) {
.def_readwrite("move_assignments", &ConstructorStats::move_assignments) .def_readwrite("move_assignments", &ConstructorStats::move_assignments)
.def_readwrite("copy_constructions", &ConstructorStats::copy_constructions) .def_readwrite("copy_constructions", &ConstructorStats::copy_constructions)
.def_readwrite("move_constructions", &ConstructorStats::move_constructions) .def_readwrite("move_constructions", &ConstructorStats::move_constructions)
.def_static("get", (ConstructorStats &(*)(py::object)) &ConstructorStats::get, py::return_value_policy::reference_internal) .def_static("get",
(ConstructorStats & (*) (py::object)) & ConstructorStats::get,
py::return_value_policy::reference_internal)
// Not exactly ConstructorStats, but related: expose the internal pybind number of registered instances // Not exactly ConstructorStats, but related: expose the internal pybind number of
// to allow instance cleanup checks (invokes a GC first) // registered instances to allow instance cleanup checks (invokes a GC first)
.def_static("detail_reg_inst", []() { .def_static("detail_reg_inst", []() {
ConstructorStats::gc(); ConstructorStats::gc();
return py::detail::get_internals().registered_instances.size(); return py::detail::get_internals().registered_instances.size();
}) });
;
} }
PYBIND11_MODULE(pybind11_tests, m) { PYBIND11_MODULE(pybind11_tests, m) {
@ -79,13 +79,14 @@ PYBIND11_MODULE(pybind11_tests, m) {
.def("get_value", &UserType::value, "Get value using a method") .def("get_value", &UserType::value, "Get value using a method")
.def("set_value", &UserType::set, "Set value using a method") .def("set_value", &UserType::set, "Set value using a method")
.def_property("value", &UserType::value, &UserType::set, "Get/set value using a property") .def_property("value", &UserType::value, &UserType::set, "Get/set value using a property")
.def("__repr__", [](const UserType& u) { return "UserType({})"_s.format(u.value()); }); .def("__repr__", [](const UserType &u) { return "UserType({})"_s.format(u.value()); });
py::class_<IncType, UserType>(m, "IncType") py::class_<IncType, UserType>(m, "IncType")
.def(py::init<>()) .def(py::init<>())
.def(py::init<int>()) .def(py::init<int>())
.def("__repr__", [](const IncType& u) { return "IncType({})"_s.format(u.value()); }); .def("__repr__", [](const IncType &u) { return "IncType({})"_s.format(u.value()); });
for (const auto &initializer : initializers()) for (const auto &initializer : initializers()) {
initializer(m); initializer(m);
}
} }

View File

@ -1,15 +1,12 @@
#pragma once #pragma once
// This must be kept first for MSVC 2015.
// Do not remove the empty line between the #includes.
#include <pybind11/pybind11.h>
#include <pybind11/eval.h> #include <pybind11/eval.h>
#include <pybind11/pybind11.h>
#if defined(_MSC_VER) && _MSC_VER < 1910 #if defined(_MSC_VER) && _MSC_VER < 1910
// We get some really long type names here which causes MSVC 2015 to emit warnings // We get some really long type names here which causes MSVC 2015 to emit warnings
# pragma warning( \ # pragma warning( \
disable : 4503) // warning C4503: decorated name length exceeded, name was truncated disable : 4503) // NOLINT: warning C4503: decorated name length exceeded, name was truncated
#endif #endif
namespace py = pybind11; namespace py = pybind11;
@ -19,24 +16,23 @@ class test_initializer {
using Initializer = void (*)(py::module_ &); using Initializer = void (*)(py::module_ &);
public: public:
test_initializer(Initializer init); explicit test_initializer(Initializer init);
test_initializer(const char *submodule_name, Initializer init); test_initializer(const char *submodule_name, Initializer init);
}; };
#define TEST_SUBMODULE(name, variable) \ #define TEST_SUBMODULE(name, variable) \
void test_submodule_##name(py::module_ &); \ void test_submodule_##name(py::module_ &); \
test_initializer name(#name, test_submodule_##name); \ test_initializer name(#name, test_submodule_##name); \
void test_submodule_##name(py::module_ &variable) void test_submodule_##name(py::module_ &(variable))
/// Dummy type which is not exported anywhere -- something to trigger a conversion error /// Dummy type which is not exported anywhere -- something to trigger a conversion error
struct UnregisteredType { }; struct UnregisteredType {};
/// A user-defined type which is exported and can be used by any test /// A user-defined type which is exported and can be used by any test
class UserType { class UserType {
public: public:
UserType() = default; UserType() = default;
UserType(int i) : i(i) { } explicit UserType(int i) : i(i) {}
int value() const { return i; } int value() const { return i; }
void set(int set) { i = set; } void set(int set) { i = set; }
@ -50,7 +46,7 @@ class IncType : public UserType {
public: public:
using UserType::UserType; using UserType::UserType;
IncType() = default; IncType() = default;
IncType(const IncType &other) : IncType(other.value() + 1) { } IncType(const IncType &other) : IncType(other.value() + 1) {}
IncType(IncType &&) = delete; IncType(IncType &&) = delete;
IncType &operator=(const IncType &) = delete; IncType &operator=(const IncType &) = delete;
IncType &operator=(IncType &&) = delete; IncType &operator=(IncType &&) = delete;
@ -62,16 +58,21 @@ union IntFloat {
float f; float f;
}; };
/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast context. /// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast
/// Used to test recursive casters (e.g. std::tuple, stl containers). /// context. Used to test recursive casters (e.g. std::tuple, stl containers).
struct RValueCaster {}; struct RValueCaster {};
PYBIND11_NAMESPACE_BEGIN(pybind11) PYBIND11_NAMESPACE_BEGIN(pybind11)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template<> class type_caster<RValueCaster> { template <>
class type_caster<RValueCaster> {
public: public:
PYBIND11_TYPE_CASTER(RValueCaster, _x("RValueCaster")); PYBIND11_TYPE_CASTER(RValueCaster, const_name("RValueCaster"));
static handle cast(RValueCaster &&, return_value_policy, handle) { return py::str("rvalue").release(); } static handle cast(RValueCaster &&, return_value_policy, handle) {
static handle cast(const RValueCaster &, return_value_policy, handle) { return py::str("lvalue").release(); } return py::str("rvalue").release();
}
static handle cast(const RValueCaster &, return_value_policy, handle) {
return py::str("lvalue").release();
}
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(pybind11) PYBIND11_NAMESPACE_END(pybind11)
@ -85,5 +86,6 @@ void ignoreOldStyleInitWarnings(F &&body) {
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.filterwarnings("ignore", message=message, category=FutureWarning) warnings.filterwarnings("ignore", message=message, category=FutureWarning)
body() body()
)", py::dict(py::arg("body") = py::cpp_function(body))); )",
py::dict(py::arg("body") = py::cpp_function(body)));
} }

View File

@ -1,12 +1,12 @@
--extra-index-url https://antocuni.github.io/pypy-wheels/manylinux2010/ numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" and platform_python_implementation!="PyPy"
numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.6"
numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" and python_version>="3.6" numpy==1.20.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
numpy==1.19.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version=="3.6" numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
numpy==1.20.0; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.7" and python_version<"3.10" numpy==1.21.3; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.11"
py @ git+https://github.com/pytest-dev/py; python_version>="3.11"
pytest==4.6.9; python_version<"3.5" pytest==4.6.9; python_version<"3.5"
pytest==6.1.2; python_version=="3.5" pytest==6.1.2; python_version=="3.5"
pytest==6.2.1; python_version>="3.6" and python_version<="3.9" pytest==6.2.4; python_version>="3.6"
pytest @ git+https://github.com/pytest-dev/pytest@c117bc350ec1e570672fda3b2ad234fd52e72b53; python_version>="3.10"
pytest-timeout pytest-timeout
scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version<"3.6" scipy==1.2.3; platform_python_implementation!="PyPy" and python_version<"3.6"
scipy==1.5.4; (platform_python_implementation!="PyPy" or sys_platform=="linux") and python_version>="3.6" and python_version<"3.10" scipy==1.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10"

View File

@ -11,12 +11,11 @@
TEST_SUBMODULE(async_module, m) { TEST_SUBMODULE(async_module, m) {
struct DoesNotSupportAsync {}; struct DoesNotSupportAsync {};
py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync") py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync").def(py::init<>());
.def(py::init<>());
struct SupportsAsync {}; struct SupportsAsync {};
py::class_<SupportsAsync>(m, "SupportsAsync") py::class_<SupportsAsync>(m, "SupportsAsync")
.def(py::init<>()) .def(py::init<>())
.def("__await__", [](const SupportsAsync& self) -> py::object { .def("__await__", [](const SupportsAsync &self) -> py::object {
static_cast<void>(self); static_cast<void>(self);
py::object loop = py::module_::import("asyncio.events").attr("get_event_loop")(); py::object loop = py::module_::import("asyncio.events").attr("get_event_loop")();
py::object f = loop.attr("create_future")(); py::object f = loop.attr("create_future")();

View File

@ -7,22 +7,26 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
TEST_SUBMODULE(buffers, m) { TEST_SUBMODULE(buffers, m) {
// test_from_python / test_to_python: // test_from_python / test_to_python:
class Matrix { class Matrix {
public: public:
Matrix(py::ssize_t rows, py::ssize_t cols) : m_rows(rows), m_cols(cols) { Matrix(py::ssize_t rows, py::ssize_t cols) : m_rows(rows), m_cols(cols) {
print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
m_data = new float[(size_t) (rows*cols)]; // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
m_data = new float[(size_t) (rows * cols)];
memset(m_data, 0, sizeof(float) * (size_t) (rows * cols)); memset(m_data, 0, sizeof(float) * (size_t) (rows * cols));
} }
Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) { Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); print_copy_created(this,
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
m_data = new float[(size_t) (m_rows * m_cols)]; m_data = new float[(size_t) (m_rows * m_cols)];
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols)); memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
} }
@ -35,12 +39,17 @@ TEST_SUBMODULE(buffers, m) {
} }
~Matrix() { ~Matrix() {
print_destroyed(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); print_destroyed(this,
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
delete[] m_data; delete[] m_data;
} }
Matrix &operator=(const Matrix &s) { Matrix &operator=(const Matrix &s) {
print_copy_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); if (this == &s) {
return *this;
}
print_copy_assigned(this,
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
delete[] m_data; delete[] m_data;
m_rows = s.m_rows; m_rows = s.m_rows;
m_cols = s.m_cols; m_cols = s.m_cols;
@ -50,27 +59,33 @@ TEST_SUBMODULE(buffers, m) {
} }
Matrix &operator=(Matrix &&s) noexcept { Matrix &operator=(Matrix &&s) noexcept {
print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix"); print_move_assigned(this,
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
if (&s != this) { if (&s != this) {
delete[] m_data; delete[] m_data;
m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data; m_rows = s.m_rows;
s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr; m_cols = s.m_cols;
m_data = s.m_data;
s.m_rows = 0;
s.m_cols = 0;
s.m_data = nullptr;
} }
return *this; return *this;
} }
float operator()(py::ssize_t i, py::ssize_t j) const { float operator()(py::ssize_t i, py::ssize_t j) const {
return m_data[(size_t) (i*m_cols + j)]; return m_data[(size_t) (i * m_cols + j)];
} }
float &operator()(py::ssize_t i, py::ssize_t j) { float &operator()(py::ssize_t i, py::ssize_t j) {
return m_data[(size_t) (i*m_cols + j)]; return m_data[(size_t) (i * m_cols + j)];
} }
float *data() { return m_data; } float *data() { return m_data; }
py::ssize_t rows() const { return m_rows; } py::ssize_t rows() const { return m_rows; }
py::ssize_t cols() const { return m_cols; } py::ssize_t cols() const { return m_cols; }
private: private:
py::ssize_t m_rows; py::ssize_t m_rows;
py::ssize_t m_cols; py::ssize_t m_cols;
@ -81,10 +96,11 @@ TEST_SUBMODULE(buffers, m) {
/// Construct from a buffer /// Construct from a buffer
.def(py::init([](const py::buffer &b) { .def(py::init([](const py::buffer &b) {
py::buffer_info info = b.request(); py::buffer_info info = b.request();
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2) if (info.format != py::format_descriptor<float>::format() || info.ndim != 2) {
throw std::runtime_error("Incompatible buffer format!"); throw std::runtime_error("Incompatible buffer format!");
}
auto v = new Matrix(info.shape[0], info.shape[1]); auto *v = new Matrix(info.shape[0], info.shape[1]);
memcpy(v->data(), info.ptr, sizeof(float) * (size_t) (v->rows() * v->cols())); memcpy(v->data(), info.ptr, sizeof(float) * (size_t) (v->rows() * v->cols()));
return v; return v;
})) }))
@ -95,35 +111,34 @@ TEST_SUBMODULE(buffers, m) {
/// Bare bones interface /// Bare bones interface
.def("__getitem__", .def("__getitem__",
[](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) { [](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) {
if (i.first >= m.rows() || i.second >= m.cols()) if (i.first >= m.rows() || i.second >= m.cols()) {
throw py::index_error(); throw py::index_error();
}
return m(i.first, i.second); return m(i.first, i.second);
}) })
.def("__setitem__", .def("__setitem__",
[](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) { [](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) {
if (i.first >= m.rows() || i.second >= m.cols()) if (i.first >= m.rows() || i.second >= m.cols()) {
throw py::index_error(); throw py::index_error();
}
m(i.first, i.second) = v; m(i.first, i.second) = v;
}) })
/// Provide buffer access /// Provide buffer access
.def_buffer([](Matrix &m) -> py::buffer_info { .def_buffer([](Matrix &m) -> py::buffer_info {
return py::buffer_info( return py::buffer_info(
m.data(), /* Pointer to buffer */ m.data(), /* Pointer to buffer */
{ m.rows(), m.cols() }, /* Buffer dimensions */ {m.rows(), m.cols()}, /* Buffer dimensions */
{ sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */ {sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
sizeof(float) } sizeof(float)});
);
}); });
// test_inherited_protocol // test_inherited_protocol
class SquareMatrix : public Matrix { class SquareMatrix : public Matrix {
public: public:
SquareMatrix(py::ssize_t n) : Matrix(n, n) { } explicit SquareMatrix(py::ssize_t n) : Matrix(n, n) {}
}; };
// Derived classes inherit the buffer protocol and the buffer access function // Derived classes inherit the buffer protocol and the buffer access function
py::class_<SquareMatrix, Matrix>(m, "SquareMatrix") py::class_<SquareMatrix, Matrix>(m, "SquareMatrix").def(py::init<py::ssize_t>());
.def(py::init<py::ssize_t>());
// test_pointer_to_member_fn // test_pointer_to_member_fn
// Tests that passing a pointer to member to the base class works in // Tests that passing a pointer to member to the base class works in
@ -132,8 +147,8 @@ TEST_SUBMODULE(buffers, m) {
int32_t value = 0; int32_t value = 0;
py::buffer_info get_buffer_info() { py::buffer_info get_buffer_info() {
return py::buffer_info(&value, sizeof(value), return py::buffer_info(
py::format_descriptor<int32_t>::format(), 1); &value, sizeof(value), py::format_descriptor<int32_t>::format(), 1);
} }
}; };
py::class_<Buffer>(m, "Buffer", py::buffer_protocol()) py::class_<Buffer>(m, "Buffer", py::buffer_protocol())
@ -141,7 +156,6 @@ TEST_SUBMODULE(buffers, m) {
.def_readwrite("value", &Buffer::value) .def_readwrite("value", &Buffer::value)
.def_buffer(&Buffer::get_buffer_info); .def_buffer(&Buffer::get_buffer_info);
class ConstBuffer { class ConstBuffer {
std::unique_ptr<int32_t> value; std::unique_ptr<int32_t> value;
@ -150,8 +164,8 @@ TEST_SUBMODULE(buffers, m) {
void set_value(int32_t v) { *value = v; } void set_value(int32_t v) { *value = v; }
py::buffer_info get_buffer_info() const { py::buffer_info get_buffer_info() const {
return py::buffer_info(value.get(), sizeof(*value), return py::buffer_info(
py::format_descriptor<int32_t>::format(), 1); value.get(), sizeof(*value), py::format_descriptor<int32_t>::format(), 1);
} }
ConstBuffer() : value(new int32_t{0}) {} ConstBuffer() : value(new int32_t{0}) {}
@ -161,7 +175,7 @@ TEST_SUBMODULE(buffers, m) {
.def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value) .def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value)
.def_buffer(&ConstBuffer::get_buffer_info); .def_buffer(&ConstBuffer::get_buffer_info);
struct DerivedBuffer : public Buffer { }; struct DerivedBuffer : public Buffer {};
py::class_<DerivedBuffer>(m, "DerivedBuffer", py::buffer_protocol()) py::class_<DerivedBuffer>(m, "DerivedBuffer", py::buffer_protocol())
.def(py::init<>()) .def(py::init<>())
.def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value) .def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
@ -169,11 +183,9 @@ TEST_SUBMODULE(buffers, m) {
struct BufferReadOnly { struct BufferReadOnly {
const uint8_t value = 0; const uint8_t value = 0;
BufferReadOnly(uint8_t value): value(value) {} explicit BufferReadOnly(uint8_t value) : value(value) {}
py::buffer_info get_buffer_info() { py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1); }
return py::buffer_info(&value, 1);
}
}; };
py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol()) py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol())
.def(py::init<uint8_t>()) .def(py::init<uint8_t>())
@ -183,9 +195,7 @@ TEST_SUBMODULE(buffers, m) {
uint8_t value = 0; uint8_t value = 0;
bool readonly = false; bool readonly = false;
py::buffer_info get_buffer_info() { py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1, readonly); }
return py::buffer_info(&value, 1, readonly);
}
}; };
py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol()) py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol())
.def(py::init<>()) .def(py::init<>())
@ -204,9 +214,11 @@ TEST_SUBMODULE(buffers, m) {
.def_readonly("strides", &py::buffer_info::strides) .def_readonly("strides", &py::buffer_info::strides)
.def_readonly("readonly", &py::buffer_info::readonly) .def_readonly("readonly", &py::buffer_info::readonly)
.def("__repr__", [](py::handle self) { .def("__repr__", [](py::handle self) {
return py::str("itemsize={0.itemsize!r}, size={0.size!r}, format={0.format!r}, ndim={0.ndim!r}, shape={0.shape!r}, strides={0.strides!r}, readonly={0.readonly!r}").format(self); return py::str("itemsize={0.itemsize!r}, size={0.size!r}, format={0.format!r}, "
}) "ndim={0.ndim!r}, shape={0.shape!r}, strides={0.strides!r}, "
; "readonly={0.readonly!r}")
.format(self);
});
m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); }); m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); });
} }

View File

@ -1,14 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import ctypes
import io import io
import struct import struct
import ctypes
import pytest import pytest
import env # noqa: F401 import env
from pybind11_tests import buffers as m
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
from pybind11_tests import buffers as m
np = pytest.importorskip("numpy") np = pytest.importorskip("numpy")
@ -37,6 +36,10 @@ def test_from_python():
# https://foss.heptapod.net/pypy/pypy/-/issues/2444 # https://foss.heptapod.net/pypy/pypy/-/issues/2444
# TODO: fix on recent PyPy
@pytest.mark.xfail(
env.PYPY, reason="PyPy 7.3.7 doesn't clear this anymore", strict=False
)
def test_to_python(): def test_to_python():
mat = m.Matrix(5, 4) mat = m.Matrix(5, 4)
assert memoryview(mat).shape == (5, 4) assert memoryview(mat).shape == (5, 4)

View File

@ -7,57 +7,67 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/complex.h> #include <pybind11/complex.h>
#if defined(_MSC_VER) #include "pybind11_tests.h"
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#endif
struct ConstRefCasted { struct ConstRefCasted {
int tag; int tag;
}; };
PYBIND11_NAMESPACE_BEGIN(pybind11) PYBIND11_NAMESPACE_BEGIN(pybind11)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <> template <>
class type_caster<ConstRefCasted> { class type_caster<ConstRefCasted> {
public: public:
static constexpr auto name = _x<ConstRefCasted>(); static constexpr auto name = const_name<ConstRefCasted>();
// Input is unimportant, a new value will always be constructed based on the // Input is unimportant, a new value will always be constructed based on the
// cast operator. // cast operator.
bool load(handle, bool) { return true; } bool load(handle, bool) { return true; }
operator ConstRefCasted &&() { explicit operator ConstRefCasted &&() {
value = {1}; value = {1};
// NOLINTNEXTLINE(performance-move-const-arg) // NOLINTNEXTLINE(performance-move-const-arg)
return std::move(value); return std::move(value);
} }
operator ConstRefCasted&() { value = {2}; return value; } explicit operator ConstRefCasted &() {
operator ConstRefCasted*() { value = {3}; return &value; } value = {2};
return value;
}
explicit operator ConstRefCasted *() {
value = {3};
return &value;
}
operator const ConstRefCasted&() { value = {4}; return value; } explicit operator const ConstRefCasted &() {
operator const ConstRefCasted*() { value = {5}; return &value; } value = {4};
return value;
}
explicit operator const ConstRefCasted *() {
value = {5};
return &value;
}
// custom cast_op to explicitly propagate types to the conversion operators. // custom cast_op to explicitly propagate types to the conversion operators.
template <typename T_> template <typename T_>
using cast_op_type = using cast_op_type =
/// const /// const
conditional_t< conditional_t<
std::is_same<remove_reference_t<T_>, const ConstRefCasted*>::value, const ConstRefCasted*, std::is_same<remove_reference_t<T_>, const ConstRefCasted *>::value,
conditional_t< const ConstRefCasted *,
std::is_same<T_, const ConstRefCasted&>::value, const ConstRefCasted&, conditional_t<
/// non-const std::is_same<T_, const ConstRefCasted &>::value,
conditional_t< const ConstRefCasted &,
std::is_same<remove_reference_t<T_>, ConstRefCasted*>::value, ConstRefCasted*, /// non-const
conditional_t< conditional_t<std::is_same<remove_reference_t<T_>, ConstRefCasted *>::value,
std::is_same<T_, ConstRefCasted&>::value, ConstRefCasted&, ConstRefCasted *,
/* else */ConstRefCasted&&>>>>; conditional_t<std::is_same<T_, ConstRefCasted &>::value,
ConstRefCasted &,
/* else */ ConstRefCasted &&>>>>;
private: private:
ConstRefCasted value = {0}; ConstRefCasted value = {0};
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(pybind11) PYBIND11_NAMESPACE_END(pybind11)
@ -67,27 +77,49 @@ TEST_SUBMODULE(builtin_casters, m) {
m.def("string_roundtrip", [](const char *s) { return s; }); m.def("string_roundtrip", [](const char *s) { return s; });
// test_unicode_conversion // test_unicode_conversion
// Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte // Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null
char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, mathbfA32 = 0x1d400 /*𝐀*/; // byte
char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/,
mathbfA32 = 0x1d400 /*𝐀*/;
char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82,
mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00;
std::wstring wstr; std::wstring wstr;
wstr.push_back(0x61); // a wstr.push_back(0x61); // a
wstr.push_back(0x2e18); // ⸘ wstr.push_back(0x2e18); // ⸘
if (sizeof(wchar_t) == 2) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16 if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32 wstr.push_back(mathbfA16_1);
wstr.push_back(mathbfA16_2);
} // 𝐀, utf16
else {
wstr.push_back((wchar_t) mathbfA32);
} // 𝐀, utf32
wstr.push_back(0x7a); // z wstr.push_back(0x7a); // z
m.def("good_utf8_string", []() { return std::string((const char*)u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀 m.def("good_utf8_string", []() {
m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // b‽🎂𝐀z return std::string((const char *) u8"Say utf8\u203d \U0001f382 \U0001d400");
m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // a𝐀🎂‽z }); // Say utf8‽ 🎂 𝐀
m.def("good_utf16_string", [=]() {
return std::u16string({b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16});
}); // b‽🎂𝐀z
m.def("good_utf32_string", [=]() {
return std::u32string({a32, mathbfA32, cake32, ib32, z32});
}); // a𝐀🎂‽z
m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z
m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); }); m.def("bad_utf8_string", []() {
m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); }); return std::string("abc\xd0"
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError "def");
if (PY_MAJOR_VERSION >= 3) });
m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); }); m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); });
if (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2) #if PY_MAJOR_VERSION >= 3
m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); }); // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger
// UnicodeDecodeError
m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); });
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
m.def("bad_wchar_string", [=]() {
return std::wstring({wchar_t(0x61), wchar_t(0xd800)});
});
}
#endif
m.def("u8_Z", []() -> char { return 'Z'; }); m.def("u8_Z", []() -> char { return 'Z'; });
m.def("u8_eacute", []() -> char { return '\xe9'; }); m.def("u8_eacute", []() -> char { return '\xe9'; });
m.def("u16_ibang", [=]() -> char16_t { return ib16; }); m.def("u16_ibang", [=]() -> char16_t { return ib16; });
@ -109,8 +141,13 @@ TEST_SUBMODULE(builtin_casters, m) {
#ifdef PYBIND11_HAS_U8STRING #ifdef PYBIND11_HAS_U8STRING
m.attr("has_u8string") = true; m.attr("has_u8string") = true;
m.def("good_utf8_u8string", []() { return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀 m.def("good_utf8_u8string", []() {
m.def("bad_utf8_u8string", []() { return std::u8string((const char8_t*)"abc\xd0" "def"); }); return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400");
}); // Say utf8‽ 🎂 𝐀
m.def("bad_utf8_u8string", []() {
return std::u8string((const char8_t *) "abc\xd0"
"def");
});
m.def("u8_char8_Z", []() -> char8_t { return u8'Z'; }); m.def("u8_char8_Z", []() -> char8_t { return u8'Z'; });
@ -122,21 +159,74 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_string_view // test_string_view
#ifdef PYBIND11_HAS_STRING_VIEW #ifdef PYBIND11_HAS_STRING_VIEW
m.attr("has_string_view") = true; m.attr("has_string_view") = true;
m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); }); m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); });
m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); }); m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); });
m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); }); m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); });
m.def("string_view_chars", [](std::string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; }); m.def("string_view_chars", [](std::string_view s) {
m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); py::list l;
m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); for (auto c : s) {
m.def("string_view_return", []() { return std::string_view((const char*)u8"utf8 secret \U0001f382"); }); l.append((std::uint8_t) c);
m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); }); }
m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); }); return l;
});
m.def("string_view16_chars", [](std::u16string_view s) {
py::list l;
for (auto c : s) {
l.append((int) c);
}
return l;
});
m.def("string_view32_chars", [](std::u32string_view s) {
py::list l;
for (auto c : s) {
l.append((int) c);
}
return l;
});
m.def("string_view_return",
[]() { return std::string_view((const char *) u8"utf8 secret \U0001f382"); });
m.def("string_view16_return",
[]() { return std::u16string_view(u"utf16 secret \U0001f382"); });
m.def("string_view32_return",
[]() { return std::u32string_view(U"utf32 secret \U0001f382"); });
# ifdef PYBIND11_HAS_U8STRING // The inner lambdas here are to also test implicit conversion
m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); }); using namespace std::literals;
m.def("string_view8_chars", [](std::u8string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; }); m.def("string_view_bytes",
[]() { return [](py::bytes b) { return b; }("abc \x80\x80 def"sv); });
m.def("string_view_str",
[]() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); });
m.def("string_view_from_bytes",
[](const py::bytes &b) { return [](std::string_view s) { return s; }(b); });
# if PY_MAJOR_VERSION >= 3
m.def("string_view_memoryview", []() {
static constexpr auto val = "Have some \360\237\216\202"sv;
return py::memoryview::from_memory(val);
});
# endif
# ifdef PYBIND11_HAS_U8STRING
m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); });
m.def("string_view8_chars", [](std::u8string_view s) {
py::list l;
for (auto c : s)
l.append((std::uint8_t) c);
return l;
});
m.def("string_view8_return", []() { return std::u8string_view(u8"utf8 secret \U0001f382"); }); m.def("string_view8_return", []() { return std::u8string_view(u8"utf8 secret \U0001f382"); });
# endif m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; });
# endif
struct TypeWithBothOperatorStringAndStringView {
// NOLINTNEXTLINE(google-explicit-constructor)
operator std::string() const { return "success"; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator std::string_view() const { return "failure"; }
};
m.def("bytes_from_type_with_both_operator_string_and_string_view",
[]() { return py::bytes(TypeWithBothOperatorStringAndStringView()); });
m.def("str_from_type_with_both_operator_string_and_string_view",
[]() { return py::str(TypeWithBothOperatorStringAndStringView()); });
#endif #endif
// test_integer_casting // test_integer_casting
@ -147,7 +237,8 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_int_convert // test_int_convert
m.def("int_passthrough", [](int arg) { return arg; }); m.def("int_passthrough", [](int arg) { return arg; });
m.def("int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert()); m.def(
"int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert());
// test_tuple // test_tuple
m.def( m.def(
@ -156,19 +247,27 @@ TEST_SUBMODULE(builtin_casters, m) {
return std::make_pair(input.second, input.first); return std::make_pair(input.second, input.first);
}, },
"Return a pair in reversed order"); "Return a pair in reversed order");
m.def("tuple_passthrough", [](std::tuple<bool, std::string, int> input) { m.def(
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input)); "tuple_passthrough",
}, "Return a triple in reversed order"); [](std::tuple<bool, std::string, int> input) {
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
},
"Return a triple in reversed order");
m.def("empty_tuple", []() { return std::tuple<>(); }); m.def("empty_tuple", []() { return std::tuple<>(); });
static std::pair<RValueCaster, RValueCaster> lvpair; static std::pair<RValueCaster, RValueCaster> lvpair;
static std::tuple<RValueCaster, RValueCaster, RValueCaster> lvtuple; static std::tuple<RValueCaster, RValueCaster, RValueCaster> lvtuple;
static std::pair<RValueCaster, std::tuple<RValueCaster, std::pair<RValueCaster, RValueCaster>>> lvnested; static std::pair<RValueCaster, std::tuple<RValueCaster, std::pair<RValueCaster, RValueCaster>>>
lvnested;
m.def("rvalue_pair", []() { return std::make_pair(RValueCaster{}, RValueCaster{}); }); m.def("rvalue_pair", []() { return std::make_pair(RValueCaster{}, RValueCaster{}); });
m.def("lvalue_pair", []() -> const decltype(lvpair) & { return lvpair; }); m.def("lvalue_pair", []() -> const decltype(lvpair) & { return lvpair; });
m.def("rvalue_tuple", []() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); }); m.def("rvalue_tuple",
[]() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); });
m.def("lvalue_tuple", []() -> const decltype(lvtuple) & { return lvtuple; }); m.def("lvalue_tuple", []() -> const decltype(lvtuple) & { return lvtuple; });
m.def("rvalue_nested", []() { m.def("rvalue_nested", []() {
return std::make_pair(RValueCaster{}, std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{}))); }); return std::make_pair(
RValueCaster{},
std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{})));
});
m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; }); m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; });
static std::pair<int, std::string> int_string_pair{2, "items"}; static std::pair<int, std::string> int_string_pair{2, "items"};
@ -176,11 +275,11 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_builtins_cast_return_none // test_builtins_cast_return_none
m.def("return_none_string", []() -> std::string * { return nullptr; }); m.def("return_none_string", []() -> std::string * { return nullptr; });
m.def("return_none_char", []() -> const char * { return nullptr; }); m.def("return_none_char", []() -> const char * { return nullptr; });
m.def("return_none_bool", []() -> bool * { return nullptr; }); m.def("return_none_bool", []() -> bool * { return nullptr; });
m.def("return_none_int", []() -> int * { return nullptr; }); m.def("return_none_int", []() -> int * { return nullptr; });
m.def("return_none_float", []() -> float * { return nullptr; }); m.def("return_none_float", []() -> float * { return nullptr; });
m.def("return_none_pair", []() -> std::pair<int,int> * { return nullptr; }); m.def("return_none_pair", []() -> std::pair<int, int> * { return nullptr; });
// test_none_deferred // test_none_deferred
m.def("defer_none_cstring", [](char *) { return false; }); m.def("defer_none_cstring", [](char *) { return false; });
@ -198,7 +297,8 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_bool_caster // test_bool_caster
m.def("bool_passthrough", [](bool arg) { return arg; }); m.def("bool_passthrough", [](bool arg) { return arg; });
m.def("bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert()); m.def(
"bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert());
// TODO: This should be disabled and fixed in future Intel compilers // TODO: This should be disabled and fixed in future Intel compilers
#if !defined(__INTEL_COMPILER) #if !defined(__INTEL_COMPILER)
@ -206,13 +306,15 @@ TEST_SUBMODULE(builtin_casters, m) {
// When compiled with the Intel compiler, this results in segmentation faults when importing // When compiled with the Intel compiler, this results in segmentation faults when importing
// the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when // the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when
// a newer version of icc is available. // a newer version of icc is available.
m.def("bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert()); m.def(
"bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert());
#endif #endif
// test_reference_wrapper // test_reference_wrapper
m.def("refwrap_builtin", [](std::reference_wrapper<int> p) { return 10 * p.get(); }); m.def("refwrap_builtin", [](std::reference_wrapper<int> p) { return 10 * p.get(); });
m.def("refwrap_usertype", [](std::reference_wrapper<UserType> p) { return p.get().value(); }); m.def("refwrap_usertype", [](std::reference_wrapper<UserType> p) { return p.get().value(); });
m.def("refwrap_usertype_const", [](std::reference_wrapper<const UserType> p) { return p.get().value(); }); m.def("refwrap_usertype_const",
[](std::reference_wrapper<const UserType> p) { return p.get().value(); });
m.def("refwrap_lvalue", []() -> std::reference_wrapper<UserType> { m.def("refwrap_lvalue", []() -> std::reference_wrapper<UserType> {
static UserType x(1); static UserType x(1);
@ -225,17 +327,20 @@ TEST_SUBMODULE(builtin_casters, m) {
// Not currently supported (std::pair caster has return-by-value cast operator); // Not currently supported (std::pair caster has return-by-value cast operator);
// triggers static_assert failure. // triggers static_assert failure.
//m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { }); // m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
m.def("refwrap_list", [](bool copy) { m.def(
static IncType x1(1), x2(2); "refwrap_list",
py::list l; [](bool copy) {
for (auto &f : {std::ref(x1), std::ref(x2)}) { static IncType x1(1), x2(2);
l.append(py::cast(f, copy ? py::return_value_policy::copy py::list l;
: py::return_value_policy::reference)); for (const auto &f : {std::ref(x1), std::ref(x2)}) {
} l.append(py::cast(
return l; f, copy ? py::return_value_policy::copy : py::return_value_policy::reference));
}, "copy"_a); }
return l;
},
"copy"_a);
m.def("refwrap_iiw", [](const IncType &w) { return w.value(); }); m.def("refwrap_iiw", [](const IncType &w) { return w.value(); });
m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) { m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) {
@ -252,12 +357,13 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_complex // test_complex
m.def("complex_cast", [](float x) { return "{}"_s.format(x); }); m.def("complex_cast", [](float x) { return "{}"_s.format(x); });
m.def("complex_cast", [](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); }); m.def("complex_cast",
[](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); });
// test int vs. long (Python 2) // test int vs. long (Python 2)
m.def("int_cast", []() {return (int) 42;}); m.def("int_cast", []() { return (int) 42; });
m.def("long_cast", []() {return (long) 42;}); m.def("long_cast", []() { return (long) 42; });
m.def("longlong_cast", []() {return ULLONG_MAX;}); m.def("longlong_cast", []() { return ULLONG_MAX; });
/// test void* cast operator /// test void* cast operator
m.def("test_void_caster", []() -> bool { m.def("test_void_caster", []() -> bool {
@ -268,11 +374,12 @@ TEST_SUBMODULE(builtin_casters, m) {
// Tests const/non-const propagation in cast_op. // Tests const/non-const propagation in cast_op.
m.def("takes", [](ConstRefCasted x) { return x.tag; }); m.def("takes", [](ConstRefCasted x) { return x.tag; });
m.def("takes_move", [](ConstRefCasted&& x) { return x.tag; }); m.def("takes_move", [](ConstRefCasted &&x) { return x.tag; });
m.def("takes_ptr", [](ConstRefCasted* x) { return x->tag; }); m.def("takes_ptr", [](ConstRefCasted *x) { return x->tag; });
m.def("takes_ref", [](ConstRefCasted& x) { return x.tag; }); m.def("takes_ref", [](ConstRefCasted &x) { return x.tag; });
m.def("takes_ref_wrap", [](std::reference_wrapper<ConstRefCasted> x) { return x.get().tag; }); m.def("takes_ref_wrap", [](std::reference_wrapper<ConstRefCasted> x) { return x.get().tag; });
m.def("takes_const_ptr", [](const ConstRefCasted* x) { return x->tag; }); m.def("takes_const_ptr", [](const ConstRefCasted *x) { return x->tag; });
m.def("takes_const_ref", [](const ConstRefCasted& x) { return x.tag; }); m.def("takes_const_ref", [](const ConstRefCasted &x) { return x.tag; });
m.def("takes_const_ref_wrap", [](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; }); m.def("takes_const_ref_wrap",
[](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; });
} }

View File

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env
from pybind11_tests import IncType, UserType
from pybind11_tests import builtin_casters as m from pybind11_tests import builtin_casters as m
from pybind11_tests import UserType, IncType
def test_simple_string(): def test_simple_string():
@ -207,6 +206,17 @@ def test_string_view(capture):
""" """
) )
assert m.string_view_bytes() == b"abc \x80\x80 def"
assert m.string_view_str() == u"abc ‽ def"
assert m.string_view_from_bytes(u"abc ‽ def".encode("utf-8")) == u"abc ‽ def"
if hasattr(m, "has_u8string"):
assert m.string_view8_str() == u"abc ‽ def"
if not env.PY2:
assert m.string_view_memoryview() == "Have some 🎂".encode()
assert m.bytes_from_type_with_both_operator_string_and_string_view() == b"success"
assert m.str_from_type_with_both_operator_string_and_string_view() == "success"
def test_integer_casting(): def test_integer_casting():
"""Issue #929 - out-of-range integer values shouldn't be accepted""" """Issue #929 - out-of-range integer values shouldn't be accepted"""
@ -300,7 +310,8 @@ def test_int_convert():
assert noconvert(7) == 7 assert noconvert(7) == 7
cant_convert(3.14159) cant_convert(3.14159)
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
if (3, 8) <= env.PY < (3, 10): # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
if (3, 8) <= env.PY < (3, 10) and env.CPYTHON:
with env.deprecated_call(): with env.deprecated_call():
assert convert(Int()) == 42 assert convert(Int()) == 42
else: else:
@ -335,7 +346,9 @@ def test_numpy_int_convert():
# The implicit conversion from np.float32 is undesirable but currently accepted. # The implicit conversion from np.float32 is undesirable but currently accepted.
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
if (3, 8) <= env.PY < (3, 10): # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
# https://github.com/pybind/pybind11/issues/3408
if (3, 8) <= env.PY < (3, 10) and env.CPYTHON:
with env.deprecated_call(): with env.deprecated_call():
assert convert(np.float32(3.14159)) == 3 assert convert(np.float32(3.14159)) == 3
else: else:

View File

@ -40,18 +40,17 @@ TEST_SUBMODULE(call_policies, m) {
Child(Child &&) = default; Child(Child &&) = default;
~Child() { py::print("Releasing child."); } ~Child() { py::print("Releasing child."); }
}; };
py::class_<Child>(m, "Child") py::class_<Child>(m, "Child").def(py::init<>());
.def(py::init<>());
class Parent { class Parent {
public: public:
Parent() { py::print("Allocating parent."); } Parent() { py::print("Allocating parent."); }
Parent(const Parent& parent) = default; Parent(const Parent &parent) = default;
~Parent() { py::print("Releasing parent."); } ~Parent() { py::print("Releasing parent."); }
void addChild(Child *) { } void addChild(Child *) {}
Child *returnChild() { return new Child(); } Child *returnChild() { return new Child(); }
Child *returnNullChild() { return nullptr; } Child *returnNullChild() { return nullptr; }
static Child *staticFunction(Parent*) { return new Child(); } static Child *staticFunction(Parent *) { return new Child(); }
}; };
py::class_<Parent>(m, "Parent") py::class_<Parent>(m, "Parent")
.def(py::init<>()) .def(py::init<>())
@ -62,11 +61,12 @@ TEST_SUBMODULE(call_policies, m) {
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>()) .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>()) .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>()) .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>())
.def_static( .def_static("staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
"staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
m.def("free_function", [](Parent*, Child*) {}, py::keep_alive<1, 2>()); m.def(
m.def("invalid_arg_index", []{}, py::keep_alive<0, 1>()); "free_function", [](Parent *, Child *) {}, py::keep_alive<1, 2>());
m.def(
"invalid_arg_index", [] {}, py::keep_alive<0, 1>());
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
// test_alive_gc // test_alive_gc
@ -74,29 +74,37 @@ TEST_SUBMODULE(call_policies, m) {
public: public:
using Parent::Parent; using Parent::Parent;
}; };
py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()) py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()).def(py::init<>());
.def(py::init<>());
#endif #endif
// test_call_guard // test_call_guard
m.def("unguarded_call", &CustomGuard::report_status); m.def("unguarded_call", &CustomGuard::report_status);
m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>()); m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>());
m.def("multiple_guards_correct_order", []() { m.def(
return CustomGuard::report_status() + std::string(" & ") + DependentGuard::report_status(); "multiple_guards_correct_order",
}, py::call_guard<CustomGuard, DependentGuard>()); []() {
return CustomGuard::report_status() + std::string(" & ")
+ DependentGuard::report_status();
},
py::call_guard<CustomGuard, DependentGuard>());
m.def("multiple_guards_wrong_order", []() { m.def(
return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status(); "multiple_guards_wrong_order",
}, py::call_guard<DependentGuard, CustomGuard>()); []() {
return DependentGuard::report_status() + std::string(" & ")
+ CustomGuard::report_status();
},
py::call_guard<DependentGuard, CustomGuard>());
#if defined(WITH_THREAD) && !defined(PYPY_VERSION) #if defined(WITH_THREAD) && !defined(PYPY_VERSION)
// `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well, // `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
// but it's unclear how to test it without `PyGILState_GetThisThreadState`. // but it's unclear how to test it without `PyGILState_GetThisThreadState`.
auto report_gil_status = []() { auto report_gil_status = []() {
auto is_gil_held = false; auto is_gil_held = false;
if (auto tstate = py::detail::get_thread_state_unchecked()) if (auto *tstate = py::detail::get_thread_state_unchecked()) {
is_gil_held = (tstate == PyGILState_GetThisThreadState()); is_gil_held = (tstate == PyGILState_GetThisThreadState());
}
return is_gil_held ? "GIL held" : "GIL released"; return is_gil_held ? "GIL held" : "GIL released";
}; };

View File

@ -2,9 +2,8 @@
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import call_policies as m
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
from pybind11_tests import call_policies as m
@pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False) @pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False)

View File

@ -7,11 +7,12 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/functional.h> #include <pybind11/functional.h>
#include <thread>
#include "constructor_stats.h"
#include "pybind11_tests.h"
#include <thread>
int dummy_function(int i) { return i + 1; } int dummy_function(int i) { return i + 1; }
@ -20,11 +21,12 @@ TEST_SUBMODULE(callbacks, m) {
m.def("test_callback1", [](const py::object &func) { return func(); }); m.def("test_callback1", [](const py::object &func) { return func(); });
m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); }); m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); });
m.def("test_callback3", [](const std::function<int(int)> &func) { m.def("test_callback3", [](const std::function<int(int)> &func) {
return "func(43) = " + std::to_string(func(43)); }); return "func(43) = " + std::to_string(func(43));
m.def("test_callback4", []() -> std::function<int(int)> { return [](int i) { return i+1; }; });
m.def("test_callback5", []() {
return py::cpp_function([](int i) { return i+1; }, py::arg("number"));
}); });
m.def("test_callback4",
[]() -> std::function<int(int)> { return [](int i) { return i + 1; }; });
m.def("test_callback5",
[]() { return py::cpp_function([](int i) { return i + 1; }, py::arg("number")); });
// test_keyword_args_and_generalized_unpacking // test_keyword_args_and_generalized_unpacking
m.def("test_tuple_unpacking", [](const py::function &f) { m.def("test_tuple_unpacking", [](const py::function &f) {
@ -34,9 +36,9 @@ TEST_SUBMODULE(callbacks, m) {
}); });
m.def("test_dict_unpacking", [](const py::function &f) { m.def("test_dict_unpacking", [](const py::function &f) {
auto d1 = py::dict("key"_a="value", "a"_a=1); auto d1 = py::dict("key"_a = "value", "a"_a = 1);
auto d2 = py::dict(); auto d2 = py::dict();
auto d3 = py::dict("b"_a=2); auto d3 = py::dict("b"_a = 2);
return f("positional", 1, **d1, **d2, **d3); return f("positional", 1, **d1, **d2, **d3);
}); });
@ -44,32 +46,40 @@ TEST_SUBMODULE(callbacks, m) {
m.def("test_unpacking_and_keywords1", [](const py::function &f) { m.def("test_unpacking_and_keywords1", [](const py::function &f) {
auto args = py::make_tuple(2); auto args = py::make_tuple(2);
auto kwargs = py::dict("d"_a=4); auto kwargs = py::dict("d"_a = 4);
return f(1, *args, "c"_a=3, **kwargs); return f(1, *args, "c"_a = 3, **kwargs);
}); });
m.def("test_unpacking_and_keywords2", [](const py::function &f) { m.def("test_unpacking_and_keywords2", [](const py::function &f) {
auto kwargs1 = py::dict("a"_a=1); auto kwargs1 = py::dict("a"_a = 1);
auto kwargs2 = py::dict("c"_a=3, "d"_a=4); auto kwargs2 = py::dict("c"_a = 3, "d"_a = 4);
return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5, return f("positional",
"key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5); *py::make_tuple(1),
2,
*py::make_tuple(3, 4),
5,
"key"_a = "value",
**kwargs1,
"b"_a = 2,
**kwargs2,
"e"_a = 5);
}); });
m.def("test_unpacking_error1", [](const py::function &f) { m.def("test_unpacking_error1", [](const py::function &f) {
auto kwargs = py::dict("x"_a=3); auto kwargs = py::dict("x"_a = 3);
return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword return f("x"_a = 1, "y"_a = 2, **kwargs); // duplicate ** after keyword
}); });
m.def("test_unpacking_error2", [](const py::function &f) { m.def("test_unpacking_error2", [](const py::function &f) {
auto kwargs = py::dict("x"_a=3); auto kwargs = py::dict("x"_a = 3);
return f(**kwargs, "x"_a=1); // duplicate keyword after ** return f(**kwargs, "x"_a = 1); // duplicate keyword after **
}); });
m.def("test_arg_conversion_error1", m.def("test_arg_conversion_error1",
[](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); }); [](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); });
m.def("test_arg_conversion_error2", [](const py::function &f) { m.def("test_arg_conversion_error2", [](const py::function &f) {
f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567); f(234, "expected_name"_a = UnregisteredType(), "kw"_a = 567);
}); });
// test_lambda_closure_cleanup // test_lambda_closure_cleanup
@ -81,30 +91,74 @@ TEST_SUBMODULE(callbacks, m) {
}; };
// Export the payload constructor statistics for testing purposes: // Export the payload constructor statistics for testing purposes:
m.def("payload_cstats", &ConstructorStats::get<Payload>); m.def("payload_cstats", &ConstructorStats::get<Payload>);
/* Test cleanup of lambda closure */ m.def("test_lambda_closure_cleanup", []() -> std::function<void()> {
m.def("test_cleanup", []() -> std::function<void()> {
Payload p; Payload p;
// In this situation, `Func` in the implementation of
// `cpp_function::initialize` is NOT trivially destructible.
return [p]() { return [p]() {
/* p should be cleaned up when the returned function is garbage collected */ /* p should be cleaned up when the returned function is garbage collected */
(void) p; (void) p;
}; };
}); });
class CppCallable {
public:
CppCallable() { track_default_created(this); }
~CppCallable() { track_destroyed(this); }
CppCallable(const CppCallable &) { track_copy_created(this); }
CppCallable(CppCallable &&) noexcept { track_move_created(this); }
void operator()() {}
};
m.def("test_cpp_callable_cleanup", []() {
// Related issue: https://github.com/pybind/pybind11/issues/3228
// Related PR: https://github.com/pybind/pybind11/pull/3229
py::list alive_counts;
ConstructorStats &stat = ConstructorStats::get<CppCallable>();
alive_counts.append(stat.alive());
{
CppCallable cpp_callable;
alive_counts.append(stat.alive());
{
// In this situation, `Func` in the implementation of
// `cpp_function::initialize` IS trivially destructible,
// only `capture` is not.
py::cpp_function py_func(cpp_callable);
py::detail::silence_unused_warnings(py_func);
alive_counts.append(stat.alive());
}
alive_counts.append(stat.alive());
{
py::cpp_function py_func(std::move(cpp_callable));
py::detail::silence_unused_warnings(py_func);
alive_counts.append(stat.alive());
}
alive_counts.append(stat.alive());
}
alive_counts.append(stat.alive());
return alive_counts;
});
// test_cpp_function_roundtrip // test_cpp_function_roundtrip
/* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */ /* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
m.def("dummy_function", &dummy_function); m.def("dummy_function", &dummy_function);
m.def("dummy_function_overloaded", [](int i, int j) { return i + j; }); m.def("dummy_function_overloaded", [](int i, int j) { return i + j; });
m.def("dummy_function_overloaded", &dummy_function); m.def("dummy_function_overloaded", &dummy_function);
m.def("dummy_function2", [](int i, int j) { return i + j; }); m.def("dummy_function2", [](int i, int j) { return i + j; });
m.def("roundtrip", [](std::function<int(int)> f, bool expect_none = false) { m.def(
if (expect_none && f) "roundtrip",
throw std::runtime_error("Expected None to be converted to empty std::function"); [](std::function<int(int)> f, bool expect_none = false) {
return f; if (expect_none && f) {
}, py::arg("f"), py::arg("expect_none")=false); throw std::runtime_error("Expected None to be converted to empty std::function");
}
return f;
},
py::arg("f"),
py::arg("expect_none") = false);
m.def("test_dummy_function", [](const std::function<int(int)> &f) -> std::string { m.def("test_dummy_function", [](const std::function<int(int)> &f) -> std::string {
using fn_type = int (*)(int); using fn_type = int (*)(int);
auto result = f.target<fn_type>(); const auto *result = f.target<fn_type>();
if (!result) { if (!result) {
auto r = f(1); auto r = f(1);
return "can't convert to function pointer: eval(1) = " + std::to_string(r); return "can't convert to function pointer: eval(1) = " + std::to_string(r);
@ -114,7 +168,6 @@ TEST_SUBMODULE(callbacks, m) {
return "matches dummy_function: eval(1) = " + std::to_string(r); return "matches dummy_function: eval(1) = " + std::to_string(r);
} }
return "argument does NOT match dummy_function. This should never happen!"; return "argument does NOT match dummy_function. This should never happen!";
}); });
class AbstractBase { class AbstractBase {
@ -146,7 +199,7 @@ TEST_SUBMODULE(callbacks, m) {
// test_movable_object // test_movable_object
m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) { m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) {
auto x = MovableObject(); auto x = MovableObject();
f(x); // lvalue reference shouldn't move out object f(x); // lvalue reference shouldn't move out object
return x.valid; // must still return `true` return x.valid; // must still return `true`
}); });
@ -158,9 +211,10 @@ TEST_SUBMODULE(callbacks, m) {
// This checks that builtin functions can be passed as callbacks // This checks that builtin functions can be passed as callbacks
// rather than throwing RuntimeError due to trying to extract as capsule // rather than throwing RuntimeError due to trying to extract as capsule
m.def("test_sum_builtin", [](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) { m.def("test_sum_builtin",
return sum_builtin(i); [](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) {
}); return sum_builtin(i);
});
// test async Python callbacks // test async Python callbacks
using callback_f = std::function<void(int)>; using callback_f = std::function<void(int)>;
@ -176,8 +230,9 @@ TEST_SUBMODULE(callbacks, m) {
}; };
// spawn worker threads // spawn worker threads
for (auto i : work) for (auto i : work) {
start_f(py::cast<int>(i)); start_f(py::cast<int>(i));
}
}); });
m.def("callback_num_times", [](const py::function &f, std::size_t num) { m.def("callback_num_times", [](const py::function &f, std::size_t num) {

View File

@ -1,9 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest
from pybind11_tests import callbacks as m
from threading import Thread
import time import time
import env # NOQA: F401 from threading import Thread
import pytest
import env # noqa: F401
from pybind11_tests import callbacks as m
def test_callbacks(): def test_callbacks():
@ -77,13 +79,18 @@ def test_keyword_args_and_generalized_unpacking():
def test_lambda_closure_cleanup(): def test_lambda_closure_cleanup():
m.test_cleanup() m.test_lambda_closure_cleanup()
cstats = m.payload_cstats() cstats = m.payload_cstats()
assert cstats.alive() == 0 assert cstats.alive() == 0
assert cstats.copy_constructions == 1 assert cstats.copy_constructions == 1
assert cstats.move_constructions >= 1 assert cstats.move_constructions >= 1
def test_cpp_callable_cleanup():
alive_counts = m.test_cpp_callable_cleanup()
assert alive_counts == [0, 1, 2, 1, 2, 1, 0]
def test_cpp_function_roundtrip(): def test_cpp_function_roundtrip():
"""Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer""" """Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer"""

View File

@ -8,21 +8,20 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/chrono.h> #include <pybind11/chrono.h>
#include "pybind11_tests.h"
#include <chrono> #include <chrono>
struct different_resolutions { struct different_resolutions {
using time_point_h = std::chrono::time_point< using time_point_h = std::chrono::time_point<std::chrono::system_clock, std::chrono::hours>;
std::chrono::system_clock, std::chrono::hours>; using time_point_m = std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>;
using time_point_m = std::chrono::time_point< using time_point_s = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
std::chrono::system_clock, std::chrono::minutes>; using time_point_ms
using time_point_s = std::chrono::time_point< = std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>;
std::chrono::system_clock, std::chrono::seconds>; using time_point_us
using time_point_ms = std::chrono::time_point< = std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds>;
std::chrono::system_clock, std::chrono::milliseconds>;
using time_point_us = std::chrono::time_point<
std::chrono::system_clock, std::chrono::microseconds>;
time_point_h timestamp_h; time_point_h timestamp_h;
time_point_m timestamp_m; time_point_m timestamp_m;
time_point_s timestamp_s; time_point_s timestamp_s;
@ -65,12 +64,11 @@ TEST_SUBMODULE(chrono, m) {
// Roundtrip a duration in microseconds from a float argument // Roundtrip a duration in microseconds from a float argument
m.def("test_chrono7", [](std::chrono::microseconds t) { return t; }); m.def("test_chrono7", [](std::chrono::microseconds t) { return t; });
// Float durations (issue #719) // Float durations (issue #719)
m.def("test_chrono_float_diff", [](std::chrono::duration<float> a, std::chrono::duration<float> b) { m.def("test_chrono_float_diff",
return a - b; }); [](std::chrono::duration<float> a, std::chrono::duration<float> b) { return a - b; });
m.def("test_nano_timepoint", [](timestamp start, timespan delta) -> timestamp { m.def("test_nano_timepoint",
return start + delta; [](timestamp start, timespan delta) -> timestamp { return start + delta; });
});
// Test different resolutions // Test different resolutions
py::class_<different_resolutions>(m, "different_resolutions") py::class_<different_resolutions>(m, "different_resolutions")
@ -79,6 +77,5 @@ TEST_SUBMODULE(chrono, m) {
.def_readwrite("timestamp_m", &different_resolutions::timestamp_m) .def_readwrite("timestamp_m", &different_resolutions::timestamp_m)
.def_readwrite("timestamp_s", &different_resolutions::timestamp_s) .def_readwrite("timestamp_s", &different_resolutions::timestamp_s)
.def_readwrite("timestamp_ms", &different_resolutions::timestamp_ms) .def_readwrite("timestamp_ms", &different_resolutions::timestamp_ms)
.def_readwrite("timestamp_us", &different_resolutions::timestamp_us) .def_readwrite("timestamp_us", &different_resolutions::timestamp_us);
;
} }

View File

@ -1,9 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from pybind11_tests import chrono as m
import datetime import datetime
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import chrono as m
def test_chrono_system_clock(): def test_chrono_system_clock():

View File

@ -11,23 +11,25 @@
// Intel compiler requires a separate header file to support aligned new operators // Intel compiler requires a separate header file to support aligned new operators
// and does not set the __cpp_aligned_new feature macro. // and does not set the __cpp_aligned_new feature macro.
// This header needs to be included before pybind11. // This header needs to be included before pybind11.
#include <aligned_new> # include <aligned_new>
#endif #endif
#include "pybind11_tests.h" #include <pybind11/stl.h>
#include "constructor_stats.h" #include "constructor_stats.h"
#include "local_bindings.h" #include "local_bindings.h"
#include <pybind11/stl.h> #include "pybind11_tests.h"
#include <utility> #include <utility>
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier # pragma warning(disable : 4324)
// warning C4324: structure was padded due to alignment specifier
#endif #endif
// test_brace_initialization // test_brace_initialization
struct NoBraceInitialization { struct NoBraceInitialization {
NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {} explicit NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
template <typename T> template <typename T>
NoBraceInitialization(std::initializer_list<T> l) : vec(l) {} NoBraceInitialization(std::initializer_list<T> l) : vec(l) {}
@ -47,10 +49,26 @@ TEST_SUBMODULE(class_, m) {
} }
~NoConstructor() { print_destroyed(this); } ~NoConstructor() { print_destroyed(this); }
}; };
struct NoConstructorNew {
NoConstructorNew() = default;
NoConstructorNew(const NoConstructorNew &) = default;
NoConstructorNew(NoConstructorNew &&) = default;
static NoConstructorNew *new_instance() {
auto *ptr = new NoConstructorNew();
print_created(ptr, "via new_instance");
return ptr;
}
~NoConstructorNew() { print_destroyed(this); }
};
py::class_<NoConstructor>(m, "NoConstructor") py::class_<NoConstructor>(m, "NoConstructor")
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance"); .def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
py::class_<NoConstructorNew>(m, "NoConstructorNew")
.def(py::init([](const NoConstructorNew &self) { return self; })) // Need a NOOP __init__
.def_static("__new__",
[](const py::object &) { return NoConstructorNew::new_instance(); });
// test_inheritance // test_inheritance
class Pet { class Pet {
public: public:
@ -58,6 +76,7 @@ TEST_SUBMODULE(class_, m) {
: m_name(name), m_species(species) {} : m_name(name), m_species(species) {}
std::string name() const { return m_name; } std::string name() const { return m_name; }
std::string species() const { return m_species; } std::string species() const { return m_species; }
private: private:
std::string m_name; std::string m_name;
std::string m_species; std::string m_species;
@ -65,18 +84,18 @@ TEST_SUBMODULE(class_, m) {
class Dog : public Pet { class Dog : public Pet {
public: public:
Dog(const std::string &name) : Pet(name, "dog") {} explicit Dog(const std::string &name) : Pet(name, "dog") {}
std::string bark() const { return "Woof!"; } std::string bark() const { return "Woof!"; }
}; };
class Rabbit : public Pet { class Rabbit : public Pet {
public: public:
Rabbit(const std::string &name) : Pet(name, "parrot") {} explicit Rabbit(const std::string &name) : Pet(name, "parrot") {}
}; };
class Hamster : public Pet { class Hamster : public Pet {
public: public:
Hamster(const std::string &name) : Pet(name, "rodent") {} explicit Hamster(const std::string &name) : Pet(name, "rodent") {}
}; };
class Chimera : public Pet { class Chimera : public Pet {
@ -84,27 +103,24 @@ TEST_SUBMODULE(class_, m) {
}; };
py::class_<Pet> pet_class(m, "Pet"); py::class_<Pet> pet_class(m, "Pet");
pet_class pet_class.def(py::init<std::string, std::string>())
.def(py::init<std::string, std::string>())
.def("name", &Pet::name) .def("name", &Pet::name)
.def("species", &Pet::species); .def("species", &Pet::species);
/* One way of declaring a subclass relationship: reference parent's class_ object */ /* One way of declaring a subclass relationship: reference parent's class_ object */
py::class_<Dog>(m, "Dog", pet_class) py::class_<Dog>(m, "Dog", pet_class).def(py::init<std::string>());
.def(py::init<std::string>());
/* Another way of declaring a subclass relationship: reference parent's C++ type */ /* Another way of declaring a subclass relationship: reference parent's C++ type */
py::class_<Rabbit, Pet>(m, "Rabbit") py::class_<Rabbit, Pet>(m, "Rabbit").def(py::init<std::string>());
.def(py::init<std::string>());
/* And another: list parent in class template arguments */ /* And another: list parent in class template arguments */
py::class_<Hamster, Pet>(m, "Hamster") py::class_<Hamster, Pet>(m, "Hamster").def(py::init<std::string>());
.def(py::init<std::string>());
/* Constructors are not inherited by default */ /* Constructors are not inherited by default */
py::class_<Chimera, Pet>(m, "Chimera"); py::class_<Chimera, Pet>(m, "Chimera");
m.def("pet_name_species", [](const Pet &pet) { return pet.name() + " is a " + pet.species(); }); m.def("pet_name_species",
[](const Pet &pet) { return pet.name() + " is a " + pet.species(); });
m.def("dog_bark", [](const Dog &dog) { return dog.bark(); }); m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
// test_automatic_upcasting // test_automatic_upcasting
@ -114,33 +130,35 @@ TEST_SUBMODULE(class_, m) {
BaseClass(BaseClass &&) = default; BaseClass(BaseClass &&) = default;
virtual ~BaseClass() = default; virtual ~BaseClass() = default;
}; };
struct DerivedClass1 : BaseClass { }; struct DerivedClass1 : BaseClass {};
struct DerivedClass2 : BaseClass { }; struct DerivedClass2 : BaseClass {};
py::class_<BaseClass>(m, "BaseClass").def(py::init<>()); py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>()); py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>()); py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); }); m.def("return_class_1", []() -> BaseClass * { return new DerivedClass1(); });
m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); }); m.def("return_class_2", []() -> BaseClass * { return new DerivedClass2(); });
m.def("return_class_n", [](int n) -> BaseClass* { m.def("return_class_n", [](int n) -> BaseClass * {
if (n == 1) return new DerivedClass1(); if (n == 1) {
if (n == 2) return new DerivedClass2(); return new DerivedClass1();
}
if (n == 2) {
return new DerivedClass2();
}
return new BaseClass(); return new BaseClass();
}); });
m.def("return_none", []() -> BaseClass* { return nullptr; }); m.def("return_none", []() -> BaseClass * { return nullptr; });
// test_isinstance // test_isinstance
m.def("check_instances", [](const py::list &l) { m.def("check_instances", [](const py::list &l) {
return py::make_tuple( return py::make_tuple(py::isinstance<py::tuple>(l[0]),
py::isinstance<py::tuple>(l[0]), py::isinstance<py::dict>(l[1]),
py::isinstance<py::dict>(l[1]), py::isinstance<Pet>(l[2]),
py::isinstance<Pet>(l[2]), py::isinstance<Pet>(l[3]),
py::isinstance<Pet>(l[3]), py::isinstance<Dog>(l[4]),
py::isinstance<Dog>(l[4]), py::isinstance<Rabbit>(l[5]),
py::isinstance<Rabbit>(l[5]), py::isinstance<UnregisteredType>(l[6]));
py::isinstance<UnregisteredType>(l[6])
);
}); });
struct Invalid {}; struct Invalid {};
@ -151,25 +169,24 @@ TEST_SUBMODULE(class_, m) {
// See https://github.com/pybind/pybind11/issues/2486 // See https://github.com/pybind/pybind11/issues/2486
// if (category == 2) // if (category == 2)
// return py::type::of<int>(); // return py::type::of<int>();
if (category == 1) if (category == 1) {
return py::type::of<DerivedClass1>(); return py::type::of<DerivedClass1>();
}
return py::type::of<Invalid>(); return py::type::of<Invalid>();
}); });
m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); }); m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); });
m.def("get_type_classic", [](py::handle h) { m.def("get_type_classic", [](py::handle h) { return h.get_type(); });
return h.get_type();
});
m.def("as_type", [](const py::object &ob) { return py::type(ob); }); m.def("as_type", [](const py::object &ob) { return py::type(ob); });
// test_mismatched_holder // test_mismatched_holder
struct MismatchBase1 { }; struct MismatchBase1 {};
struct MismatchDerived1 : MismatchBase1 { }; struct MismatchDerived1 : MismatchBase1 {};
struct MismatchBase2 { }; struct MismatchBase2 {};
struct MismatchDerived2 : MismatchBase2 { }; struct MismatchDerived2 : MismatchBase2 {};
m.def("mismatched_holder_1", []() { m.def("mismatched_holder_1", []() {
auto mod = py::module_::import("__main__"); auto mod = py::module_::import("__main__");
@ -179,16 +196,14 @@ TEST_SUBMODULE(class_, m) {
m.def("mismatched_holder_2", []() { m.def("mismatched_holder_2", []() {
auto mod = py::module_::import("__main__"); auto mod = py::module_::import("__main__");
py::class_<MismatchBase2>(mod, "MismatchBase2"); py::class_<MismatchBase2>(mod, "MismatchBase2");
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(
MismatchBase2>(mod, "MismatchDerived2"); mod, "MismatchDerived2");
}); });
// test_override_static // test_override_static
// #511: problem with inheritance + overwritten def_static // #511: problem with inheritance + overwritten def_static
struct MyBase { struct MyBase {
static std::unique_ptr<MyBase> make() { static std::unique_ptr<MyBase> make() { return std::unique_ptr<MyBase>(new MyBase()); }
return std::unique_ptr<MyBase>(new MyBase());
}
}; };
struct MyDerived : MyBase { struct MyDerived : MyBase {
@ -197,8 +212,7 @@ TEST_SUBMODULE(class_, m) {
} }
}; };
py::class_<MyBase>(m, "MyBase") py::class_<MyBase>(m, "MyBase").def_static("make", &MyBase::make);
.def_static("make", &MyBase::make);
py::class_<MyDerived, MyBase>(m, "MyDerived") py::class_<MyDerived, MyBase>(m, "MyDerived")
.def_static("make", &MyDerived::make) .def_static("make", &MyDerived::make)
@ -208,11 +222,10 @@ TEST_SUBMODULE(class_, m) {
struct ConvertibleFromUserType { struct ConvertibleFromUserType {
int i; int i;
ConvertibleFromUserType(UserType u) : i(u.value()) { } explicit ConvertibleFromUserType(UserType u) : i(u.value()) {}
}; };
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType") py::class_<ConvertibleFromUserType>(m, "AcceptsUserType").def(py::init<UserType>());
.def(py::init<UserType>());
py::implicitly_convertible<UserType, ConvertibleFromUserType>(); py::implicitly_convertible<UserType, ConvertibleFromUserType>();
m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; }); m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; });
@ -234,49 +247,91 @@ TEST_SUBMODULE(class_, m) {
return py::str().release().ptr(); return py::str().release().ptr();
}; };
auto def = new PyMethodDef{"f", f, METH_VARARGS, nullptr}; auto *def = new PyMethodDef{"f", f, METH_VARARGS, nullptr};
py::capsule def_capsule(def, [](void *ptr) { delete reinterpret_cast<PyMethodDef *>(ptr); }); py::capsule def_capsule(def,
return py::reinterpret_steal<py::object>(PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr())); [](void *ptr) { delete reinterpret_cast<PyMethodDef *>(ptr); });
return py::reinterpret_steal<py::object>(
PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr()));
}()); }());
// test_operator_new_delete // test_operator_new_delete
struct HasOpNewDel { struct HasOpNewDel {
std::uint64_t i; std::uint64_t i;
static void *operator new(size_t s) { py::print("A new", s); return ::operator new(s); } static void *operator new(size_t s) {
static void *operator new(size_t s, void *ptr) { py::print("A placement-new", s); return ptr; } py::print("A new", s);
static void operator delete(void *p) { py::print("A delete"); return ::operator delete(p); } return ::operator new(s);
}
static void *operator new(size_t s, void *ptr) {
py::print("A placement-new", s);
return ptr;
}
static void operator delete(void *p) {
py::print("A delete");
return ::operator delete(p);
}
}; };
struct HasOpNewDelSize { struct HasOpNewDelSize {
std::uint32_t i; std::uint32_t i;
static void *operator new(size_t s) { py::print("B new", s); return ::operator new(s); } static void *operator new(size_t s) {
static void *operator new(size_t s, void *ptr) { py::print("B placement-new", s); return ptr; } py::print("B new", s);
static void operator delete(void *p, size_t s) { py::print("B delete", s); return ::operator delete(p); } return ::operator new(s);
}
static void *operator new(size_t s, void *ptr) {
py::print("B placement-new", s);
return ptr;
}
static void operator delete(void *p, size_t s) {
py::print("B delete", s);
return ::operator delete(p);
}
}; };
struct AliasedHasOpNewDelSize { struct AliasedHasOpNewDelSize {
std::uint64_t i; std::uint64_t i;
static void *operator new(size_t s) { py::print("C new", s); return ::operator new(s); } static void *operator new(size_t s) {
static void *operator new(size_t s, void *ptr) { py::print("C placement-new", s); return ptr; } py::print("C new", s);
static void operator delete(void *p, size_t s) { py::print("C delete", s); return ::operator delete(p); } return ::operator new(s);
}
static void *operator new(size_t s, void *ptr) {
py::print("C placement-new", s);
return ptr;
}
static void operator delete(void *p, size_t s) {
py::print("C delete", s);
return ::operator delete(p);
}
virtual ~AliasedHasOpNewDelSize() = default; virtual ~AliasedHasOpNewDelSize() = default;
AliasedHasOpNewDelSize() = default; AliasedHasOpNewDelSize() = default;
AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize&) = delete; AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize &) = delete;
}; };
struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize { struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize {
PyAliasedHasOpNewDelSize() = default; PyAliasedHasOpNewDelSize() = default;
PyAliasedHasOpNewDelSize(int) { } explicit PyAliasedHasOpNewDelSize(int) {}
std::uint64_t j; std::uint64_t j;
}; };
struct HasOpNewDelBoth { struct HasOpNewDelBoth {
std::uint32_t i[8]; std::uint32_t i[8];
static void *operator new(size_t s) { py::print("D new", s); return ::operator new(s); } static void *operator new(size_t s) {
static void *operator new(size_t s, void *ptr) { py::print("D placement-new", s); return ptr; } py::print("D new", s);
static void operator delete(void *p) { py::print("D delete"); return ::operator delete(p); } return ::operator new(s);
static void operator delete(void *p, size_t s) { py::print("D wrong delete", s); return ::operator delete(p); } }
static void *operator new(size_t s, void *ptr) {
py::print("D placement-new", s);
return ptr;
}
static void operator delete(void *p) {
py::print("D delete");
return ::operator delete(p);
}
static void operator delete(void *p, size_t s) {
py::print("D wrong delete", s);
return ::operator delete(p);
}
}; };
py::class_<HasOpNewDel>(m, "HasOpNewDel").def(py::init<>()); py::class_<HasOpNewDel>(m, "HasOpNewDel").def(py::init<>());
py::class_<HasOpNewDelSize>(m, "HasOpNewDelSize").def(py::init<>()); py::class_<HasOpNewDelSize>(m, "HasOpNewDelSize").def(py::init<>());
py::class_<HasOpNewDelBoth>(m, "HasOpNewDelBoth").def(py::init<>()); py::class_<HasOpNewDelBoth>(m, "HasOpNewDelBoth").def(py::init<>());
py::class_<AliasedHasOpNewDelSize, PyAliasedHasOpNewDelSize> aliased(m, "AliasedHasOpNewDelSize"); py::class_<AliasedHasOpNewDelSize, PyAliasedHasOpNewDelSize> aliased(m,
"AliasedHasOpNewDelSize");
aliased.def(py::init<>()); aliased.def(py::init<>());
aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize)); aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize));
aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize)); aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize));
@ -330,7 +385,7 @@ TEST_SUBMODULE(class_, m) {
// [workaround(intel)] = default does not work here // [workaround(intel)] = default does not work here
// Removing or defaulting this destructor results in linking errors with the Intel compiler // Removing or defaulting this destructor results in linking errors with the Intel compiler
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827) // (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
~PublicistB() override {}; // NOLINT(modernize-use-equals-default) ~PublicistB() override{}; // NOLINT(modernize-use-equals-default)
using ProtectedB::foo; using ProtectedB::foo;
}; };
@ -380,8 +435,8 @@ TEST_SUBMODULE(class_, m) {
py::class_<Nested>(base, "Nested") py::class_<Nested>(base, "Nested")
.def(py::init<>()) .def(py::init<>())
.def("fn", [](Nested &, int, NestBase &, Nested &) {}) .def("fn", [](Nested &, int, NestBase &, Nested &) {})
.def("fa", [](Nested &, int, NestBase &, Nested &) {}, .def(
"a"_a, "b"_a, "c"_a); "fa", [](Nested &, int, NestBase &, Nested &) {}, "a"_a, "b"_a, "c"_a);
base.def("g", [](NestBase &, Nested &) {}); base.def("g", [](NestBase &, Nested &) {});
base.def("h", []() { return NestBase(); }); base.def("h", []() { return NestBase(); });
@ -391,21 +446,21 @@ TEST_SUBMODULE(class_, m) {
// generate a useful error message // generate a useful error message
struct NotRegistered {}; struct NotRegistered {};
struct StringWrapper { std::string str; }; struct StringWrapper {
std::string str;
};
m.def("test_error_after_conversions", [](int) {}); m.def("test_error_after_conversions", [](int) {});
m.def("test_error_after_conversions", m.def("test_error_after_conversions",
[](const StringWrapper &) -> NotRegistered { return {}; }); [](const StringWrapper &) -> NotRegistered { return {}; });
py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>()); py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
py::implicitly_convertible<std::string, StringWrapper>(); py::implicitly_convertible<std::string, StringWrapper>();
#if defined(PYBIND11_CPP17) #if defined(PYBIND11_CPP17)
struct alignas(1024) Aligned { struct alignas(1024) Aligned {
std::uintptr_t ptr() const { return (uintptr_t) this; } std::uintptr_t ptr() const { return (uintptr_t) this; }
}; };
py::class_<Aligned>(m, "Aligned") py::class_<Aligned>(m, "Aligned").def(py::init<>()).def("ptr", &Aligned::ptr);
.def(py::init<>()) #endif
.def("ptr", &Aligned::ptr);
#endif
// test_final // test_final
struct IsFinal final {}; struct IsFinal final {};
@ -418,9 +473,7 @@ TEST_SUBMODULE(class_, m) {
// test_exception_rvalue_abort // test_exception_rvalue_abort
struct PyPrintDestructor { struct PyPrintDestructor {
PyPrintDestructor() = default; PyPrintDestructor() = default;
~PyPrintDestructor() { ~PyPrintDestructor() { py::print("Print from destructor"); }
py::print("Print from destructor");
}
void throw_something() { throw std::runtime_error("error"); } void throw_something() { throw std::runtime_error("error"); }
}; };
py::class_<PyPrintDestructor>(m, "PyPrintDestructor") py::class_<PyPrintDestructor>(m, "PyPrintDestructor")
@ -434,8 +487,7 @@ TEST_SUBMODULE(class_, m) {
.def(py::init([]() { return &samePointer; })); .def(py::init([]() { return &samePointer; }));
struct Empty {}; struct Empty {};
py::class_<Empty>(m, "Empty") py::class_<Empty>(m, "Empty").def(py::init<>());
.def(py::init<>());
// test_base_and_derived_nested_scope // test_base_and_derived_nested_scope
struct BaseWithNested { struct BaseWithNested {
@ -477,12 +529,15 @@ TEST_SUBMODULE(class_, m) {
}); });
} }
template <int N> class BreaksBase { public: template <int N>
class BreaksBase {
public:
virtual ~BreaksBase() = default; virtual ~BreaksBase() = default;
BreaksBase() = default; BreaksBase() = default;
BreaksBase(const BreaksBase&) = delete; BreaksBase(const BreaksBase &) = delete;
}; };
template <int N> class BreaksTramp : public BreaksBase<N> {}; template <int N>
class BreaksTramp : public BreaksBase<N> {};
// These should all compile just fine: // These should all compile just fine:
using DoesntBreak1 = py::class_<BreaksBase<1>, std::unique_ptr<BreaksBase<1>>, BreaksTramp<1>>; using DoesntBreak1 = py::class_<BreaksBase<1>, std::unique_ptr<BreaksBase<1>>, BreaksTramp<1>>;
using DoesntBreak2 = py::class_<BreaksBase<2>, BreaksTramp<2>, std::unique_ptr<BreaksBase<2>>>; using DoesntBreak2 = py::class_<BreaksBase<2>, BreaksTramp<2>, std::unique_ptr<BreaksBase<2>>>;
@ -492,43 +547,83 @@ using DoesntBreak5 = py::class_<BreaksBase<5>>;
using DoesntBreak6 = py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>>; using DoesntBreak6 = py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>>;
using DoesntBreak7 = py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>>; using DoesntBreak7 = py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>>;
using DoesntBreak8 = py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>>; using DoesntBreak8 = py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>>;
#define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<N>>::value, \ #define CHECK_BASE(N) \
"DoesntBreak" #N " has wrong type!") static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<(N)>>::value, \
CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8); "DoesntBreak" #N " has wrong type!")
#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<N>>::value, \ CHECK_BASE(1);
CHECK_BASE(2);
CHECK_BASE(3);
CHECK_BASE(4);
CHECK_BASE(5);
CHECK_BASE(6);
CHECK_BASE(7);
CHECK_BASE(8);
#define CHECK_ALIAS(N) \
static_assert( \
DoesntBreak##N::has_alias \
&& std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<(N)>>::value, \
"DoesntBreak" #N " has wrong type_alias!") "DoesntBreak" #N " has wrong type_alias!")
#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \ #define CHECK_NOALIAS(N) \
"DoesntBreak" #N " has type alias, but shouldn't!") static_assert(!DoesntBreak##N::has_alias \
CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8); && std::is_void<typename DoesntBreak##N::type_alias>::value, \
#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<N>>>::value, \ "DoesntBreak" #N " has type alias, but shouldn't!")
"DoesntBreak" #N " has wrong holder_type!") CHECK_ALIAS(1);
CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique); CHECK_ALIAS(2);
CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared); CHECK_NOALIAS(3);
CHECK_ALIAS(4);
CHECK_NOALIAS(5);
CHECK_ALIAS(6);
CHECK_ALIAS(7);
CHECK_NOALIAS(8);
#define CHECK_HOLDER(N, TYPE) \
static_assert(std::is_same<typename DoesntBreak##N::holder_type, \
std::TYPE##_ptr<BreaksBase<(N)>>>::value, \
"DoesntBreak" #N " has wrong holder_type!")
CHECK_HOLDER(1, unique);
CHECK_HOLDER(2, unique);
CHECK_HOLDER(3, unique);
CHECK_HOLDER(4, unique);
CHECK_HOLDER(5, unique);
CHECK_HOLDER(6, shared);
CHECK_HOLDER(7, shared);
CHECK_HOLDER(8, shared);
// There's no nice way to test that these fail because they fail to compile; leave them here, // There's no nice way to test that these fail because they fail to compile; leave them here,
// though, so that they can be manually tested by uncommenting them (and seeing that compilation // though, so that they can be manually tested by uncommenting them (and seeing that compilation
// failures occurs). // failures occurs).
// We have to actually look into the type: the typedef alone isn't enough to instantiate the type: // We have to actually look into the type: the typedef alone isn't enough to instantiate the type:
#define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-N>>::value, \ #define CHECK_BROKEN(N) \
"Breaks1 has wrong type!"); static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-(N)>>::value, \
"Breaks1 has wrong type!");
//// Two holder classes: #ifdef PYBIND11_NEVER_DEFINED_EVER
//typedef py::class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>> Breaks1; // Two holder classes:
//CHECK_BROKEN(1); typedef py::
//// Two aliases: class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>>
//typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2; Breaks1;
//CHECK_BROKEN(2); CHECK_BROKEN(1);
//// Holder + 2 aliases // Two aliases:
//typedef py::class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>> Breaks3; typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
//CHECK_BROKEN(3); CHECK_BROKEN(2);
//// Alias + 2 holders // Holder + 2 aliases
//typedef py::class_<BreaksBase<-4>, std::unique_ptr<BreaksBase<-4>>, BreaksTramp<-4>, std::shared_ptr<BreaksBase<-4>>> Breaks4; typedef py::
//CHECK_BROKEN(4); class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>>
//// Invalid option (not a subclass or holder) Breaks3;
//typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5; CHECK_BROKEN(3);
//CHECK_BROKEN(5); // Alias + 2 holders
//// Invalid option: multiple inheritance not supported: typedef py::class_<BreaksBase<-4>,
//template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {}; std::unique_ptr<BreaksBase<-4>>,
//typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8; BreaksTramp<-4>,
//CHECK_BROKEN(8); std::shared_ptr<BreaksBase<-4>>>
Breaks4;
CHECK_BROKEN(4);
// Invalid option (not a subclass or holder)
typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5;
CHECK_BROKEN(5);
// Invalid option: multiple inheritance not supported:
template <>
struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
CHECK_BROKEN(8);
#endif

View File

@ -2,9 +2,8 @@
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import ConstructorStats, UserType
from pybind11_tests import class_ as m from pybind11_tests import class_ as m
from pybind11_tests import UserType, ConstructorStats
def test_repr(): def test_repr():
@ -26,6 +25,14 @@ def test_instance(msg):
assert cstats.alive() == 0 assert cstats.alive() == 0
def test_instance_new(msg):
instance = m.NoConstructorNew() # .__new__(m.NoConstructor.__class__)
cstats = ConstructorStats.get(m.NoConstructorNew)
assert cstats.alive() == 1
del instance
assert cstats.alive() == 0
def test_type(): def test_type():
assert m.check_type(1) == m.DerivedClass1 assert m.check_type(1) == m.DerivedClass1
with pytest.raises(RuntimeError) as execinfo: with pytest.raises(RuntimeError) as execinfo:

View File

@ -6,15 +6,17 @@ PYBIND11_EMBEDDED_MODULE(test_cmake_build, m) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
if (argc != 2) if (argc != 2) {
throw std::runtime_error("Expected test.py file as the first argument"); throw std::runtime_error("Expected test.py file as the first argument");
auto test_py_file = argv[1]; }
auto *test_py_file = argv[1];
py::scoped_interpreter guard{}; py::scoped_interpreter guard{};
auto m = py::module_::import("test_cmake_build"); auto m = py::module_::import("test_cmake_build");
if (m.attr("add")(1, 2).cast<int>() != 3) if (m.attr("add")(1, 2).cast<int>() != 3) {
throw std::runtime_error("embed.cpp failed"); throw std::runtime_error("embed.cpp failed");
}
py::module_::import("sys").attr("argv") = py::make_tuple("test.py", "embed.cpp"); py::module_::import("sys").attr("argv") = py::make_tuple("test.py", "embed.cpp");
py::eval_file(test_py_file, py::globals()); py::eval_file(test_py_file, py::globals());

View File

@ -22,5 +22,7 @@ set_target_properties(test_installed_embed PROPERTIES OUTPUT_NAME test_cmake_bui
# This may be needed to resolve header conflicts, e.g. between Python release and debug headers. # This may be needed to resolve header conflicts, e.g. between Python release and debug headers.
set_target_properties(test_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) set_target_properties(test_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON)
add_custom_target(check_installed_embed $<TARGET_FILE:test_installed_embed> add_custom_target(
${PROJECT_SOURCE_DIR}/../test.py) check_installed_embed
$<TARGET_FILE:test_installed_embed> ${PROJECT_SOURCE_DIR}/../test.py
DEPENDS test_installed_embed)

View File

@ -35,4 +35,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_function> PYTHONPATH=$<TARGET_FILE_DIR:test_installed_function>
${_Python_EXECUTABLE} ${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME}) ${PROJECT_NAME}
DEPENDS test_installed_function)

View File

@ -42,4 +42,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target> PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target>
${_Python_EXECUTABLE} ${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME}) ${PROJECT_NAME}
DEPENDS test_installed_target)

View File

@ -23,8 +23,10 @@ add_executable(test_subdirectory_embed ../embed.cpp)
target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed) target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed)
set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build) set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build)
add_custom_target(check_subdirectory_embed $<TARGET_FILE:test_subdirectory_embed> add_custom_target(
"${PROJECT_SOURCE_DIR}/../test.py") check_subdirectory_embed
$<TARGET_FILE:test_subdirectory_embed> "${PROJECT_SOURCE_DIR}/../test.py"
DEPENDS test_subdirectory_embed)
# Test custom export group -- PYBIND11_EXPORT_NAME # Test custom export group -- PYBIND11_EXPORT_NAME
add_library(test_embed_lib ../embed.cpp) add_library(test_embed_lib ../embed.cpp)

View File

@ -31,4 +31,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_function> PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_function>
${_Python_EXECUTABLE} ${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME}) ${PROJECT_NAME}
DEPENDS test_subdirectory_function)

View File

@ -37,4 +37,5 @@ add_custom_target(
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_target> PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_target>
${_Python_EXECUTABLE} ${_Python_EXECUTABLE}
${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_SOURCE_DIR}/../test.py
${PROJECT_NAME}) ${PROJECT_NAME}
DEPENDS test_subdirectory_target)

View File

@ -1,6 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys
import test_cmake_build import test_cmake_build
if str is not bytes: # If not Python2
assert isinstance(__file__, str) # Test this is properly set
assert test_cmake_build.add(1, 2) == 3 assert test_cmake_build.add(1, 2) == 3
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1])) print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))

View File

@ -0,0 +1,70 @@
// Copyright (c) 2021 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "pybind11_tests.h"
#if defined(_MSC_VER) && _MSC_VER < 1910
// MSVC 2015 fails in bizarre ways.
# define PYBIND11_SKIP_TEST_CONST_NAME
#else // Only test with MSVC 2017 or newer.
// IUT = Implementation Under Test
# define CONST_NAME_TESTS(TEST_FUNC, IUT) \
std::string TEST_FUNC(int selector) { \
switch (selector) { \
case 0: \
return IUT("").text; \
case 1: \
return IUT("A").text; \
case 2: \
return IUT("Bd").text; \
case 3: \
return IUT("Cef").text; \
case 4: \
return IUT<int>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
case 5: \
return IUT<std::string>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
case 6: \
return IUT<true>("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
case 7: \
return IUT<false>("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
case 8: \
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
return IUT<true>(IUT("D1"), IUT("D2")).text; \
case 9: \
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
return IUT<false>(IUT("E1"), IUT("E2")).text; \
case 10: \
return IUT("KeepAtEnd").text; \
default: \
break; \
} \
throw std::runtime_error("Invalid selector value."); \
}
CONST_NAME_TESTS(const_name_tests, py::detail::const_name)
# ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
CONST_NAME_TESTS(underscore_tests, py::detail::_)
# endif
#endif // MSVC >= 2017
TEST_SUBMODULE(const_name, m) {
#ifdef PYBIND11_SKIP_TEST_CONST_NAME
m.attr("const_name_tests") = "PYBIND11_SKIP_TEST_CONST_NAME";
#else
m.def("const_name_tests", const_name_tests);
#endif
#ifdef PYBIND11_SKIP_TEST_CONST_NAME
m.attr("underscore_tests") = "PYBIND11_SKIP_TEST_CONST_NAME";
#elif defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY)
m.def("underscore_tests", underscore_tests);
#else
m.attr("underscore_tests") = "PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY not defined.";
#endif
}

View File

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
import pytest
import env
from pybind11_tests import const_name as m
@pytest.mark.parametrize("func", (m.const_name_tests, m.underscore_tests))
@pytest.mark.parametrize(
"selector, expected",
enumerate(
(
"",
"A",
"Bd",
"Cef",
"%",
"%",
"T1",
"U2",
"D1",
"E2",
"KeepAtEnd",
)
),
)
def test_const_name(func, selector, expected):
if isinstance(func, type(u"") if env.PY2 else str):
pytest.skip(func)
text = func(selector)
assert text == expected

View File

@ -12,20 +12,14 @@
enum MyEnum { EFirstEntry = 1, ESecondEntry }; enum MyEnum { EFirstEntry = 1, ESecondEntry };
std::string test_function1() { std::string test_function1() { return "test_function()"; }
return "test_function()";
}
std::string test_function2(MyEnum k) { std::string test_function2(MyEnum k) { return "test_function(enum=" + std::to_string(k) + ")"; }
return "test_function(enum=" + std::to_string(k) + ")";
}
std::string test_function3(int i) { std::string test_function3(int i) { return "test_function(" + std::to_string(i) + ")"; }
return "test_function(" + std::to_string(i) + ")";
}
py::str test_function4() { return "test_function()"; } py::str test_function4() { return "test_function()"; }
py::str test_function4(char *) { return "test_function(char *)"; } py::str test_function4(char *) { return "test_function(char *)"; }
py::str test_function4(int, float) { return "test_function(int, float)"; } py::str test_function4(int, float) { return "test_function(int, float)"; }
py::str test_function4(float, int) { return "test_function(float, int)"; } py::str test_function4(float, int) { return "test_function(float, int)"; }
@ -44,50 +38,50 @@ std::string print_bytes(const py::bytes &bytes) {
return ret; return ret;
} }
// Test that we properly handle C++17 exception specifiers (which are part of the function signature // Test that we properly handle C++17 exception specifiers (which are part of the function
// in C++17). These should all still work before C++17, but don't affect the function signature. // signature in C++17). These should all still work before C++17, but don't affect the function
// signature.
namespace test_exc_sp { namespace test_exc_sp {
// [workaround(intel)] Unable to use noexcept instead of noexcept(true) // [workaround(intel)] Unable to use noexcept instead of noexcept(true)
// Make the f1 test basically the same as the f2 test in C++17 mode for the Intel compiler as // Make the f1 test basically the same as the f2 test in C++17 mode for the Intel compiler as
// it fails to compile with a plain noexcept (tested with icc (ICC) 2021.1 Beta 20200827). // it fails to compile with a plain noexcept (tested with icc (ICC) 2021.1 Beta 20200827).
#if defined(__INTEL_COMPILER) && defined(PYBIND11_CPP17) #if defined(__INTEL_COMPILER) && defined(PYBIND11_CPP17)
int f1(int x) noexcept(true) { return x+1; } int f1(int x) noexcept(true) { return x + 1; }
#else #else
int f1(int x) noexcept { return x+1; } int f1(int x) noexcept { return x + 1; }
#endif #endif
int f2(int x) noexcept(true) { return x+2; } int f2(int x) noexcept(true) { return x + 2; }
int f3(int x) noexcept(false) { return x+3; } int f3(int x) noexcept(false) { return x + 3; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated" # pragma GCC diagnostic ignored "-Wdeprecated"
#endif #endif
// NOLINTNEXTLINE(modernize-use-noexcept) // NOLINTNEXTLINE(modernize-use-noexcept)
int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true) int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true)
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
struct C { struct C {
int m1(int x) noexcept { return x-1; } int m1(int x) noexcept { return x - 1; }
int m2(int x) const noexcept { return x-2; } int m2(int x) const noexcept { return x - 2; }
int m3(int x) noexcept(true) { return x-3; } int m3(int x) noexcept(true) { return x - 3; }
int m4(int x) const noexcept(true) { return x-4; } int m4(int x) const noexcept(true) { return x - 4; }
int m5(int x) noexcept(false) { return x-5; } int m5(int x) noexcept(false) { return x - 5; }
int m6(int x) const noexcept(false) { return x-6; } int m6(int x) const noexcept(false) { return x - 6; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated" # pragma GCC diagnostic ignored "-Wdeprecated"
#endif #endif
// NOLINTNEXTLINE(modernize-use-noexcept) // NOLINTNEXTLINE(modernize-use-noexcept)
int m7(int x) throw() { return x - 7; } int m7(int x) throw() { return x - 7; }
// NOLINTNEXTLINE(modernize-use-noexcept) // NOLINTNEXTLINE(modernize-use-noexcept)
int m8(int x) const throw() { return x - 8; } int m8(int x) const throw() { return x - 8; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
}; };
} // namespace test_exc_sp } // namespace test_exc_sp
TEST_SUBMODULE(constants_and_functions, m) { TEST_SUBMODULE(constants_and_functions, m) {
// test_constants // test_constants
m.attr("some_constant") = py::int_(14); m.attr("some_constant") = py::int_(14);
@ -129,8 +123,7 @@ TEST_SUBMODULE(constants_and_functions, m) {
.def("m5", &C::m5) .def("m5", &C::m5)
.def("m6", &C::m6) .def("m6", &C::m6)
.def("m7", &C::m7) .def("m7", &C::m7)
.def("m8", &C::m8) .def("m8", &C::m8);
;
m.def("f1", f1); m.def("f1", f1);
m.def("f2", f2); m.def("f2", f2);
#if defined(__INTEL_COMPILER) #if defined(__INTEL_COMPILER)
@ -150,8 +143,12 @@ TEST_SUBMODULE(constants_and_functions, m) {
uint64_t zeros[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; uint64_t zeros[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
}; };
m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) { m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) {
LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here
m.def("should_raise", [capture](int) { return capture.zeros[9] + 33; }, py::kw_only(), py::arg()); m.def(
"should_raise",
[capture](int) { return capture.zeros[9] + 33; },
py::kw_only(),
py::arg());
}); });
m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) { m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) {
m.def( m.def(

View File

@ -8,36 +8,39 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
template <typename derived> template <typename derived>
struct empty { struct empty {
static const derived& get_one() { return instance_; } static const derived &get_one() { return instance_; }
static derived instance_; static derived instance_;
}; };
struct lacking_copy_ctor : public empty<lacking_copy_ctor> { struct lacking_copy_ctor : public empty<lacking_copy_ctor> {
lacking_copy_ctor() = default; lacking_copy_ctor() = default;
lacking_copy_ctor(const lacking_copy_ctor& other) = delete; lacking_copy_ctor(const lacking_copy_ctor &other) = delete;
}; };
template <> lacking_copy_ctor empty<lacking_copy_ctor>::instance_ = {}; template <>
lacking_copy_ctor empty<lacking_copy_ctor>::instance_ = {};
struct lacking_move_ctor : public empty<lacking_move_ctor> { struct lacking_move_ctor : public empty<lacking_move_ctor> {
lacking_move_ctor() = default; lacking_move_ctor() = default;
lacking_move_ctor(const lacking_move_ctor& other) = delete; lacking_move_ctor(const lacking_move_ctor &other) = delete;
lacking_move_ctor(lacking_move_ctor&& other) = delete; lacking_move_ctor(lacking_move_ctor &&other) = delete;
}; };
template <> lacking_move_ctor empty<lacking_move_ctor>::instance_ = {}; template <>
lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
/* Custom type caster move/copy test classes */ /* Custom type caster move/copy test classes */
class MoveOnlyInt { class MoveOnlyInt {
public: public:
MoveOnlyInt() { print_default_created(this); } MoveOnlyInt() { print_default_created(this); }
MoveOnlyInt(int v) : value{v} { print_created(this, value); } explicit MoveOnlyInt(int v) : value{v} { print_created(this, value); }
MoveOnlyInt(MoveOnlyInt &&m) noexcept { MoveOnlyInt(MoveOnlyInt &&m) noexcept {
print_move_created(this, m.value); print_move_created(this, m.value);
std::swap(value, m.value); std::swap(value, m.value);
@ -56,7 +59,7 @@ public:
class MoveOrCopyInt { class MoveOrCopyInt {
public: public:
MoveOrCopyInt() { print_default_created(this); } MoveOrCopyInt() { print_default_created(this); }
MoveOrCopyInt(int v) : value{v} { print_created(this, value); } explicit MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
MoveOrCopyInt(MoveOrCopyInt &&m) noexcept { MoveOrCopyInt(MoveOrCopyInt &&m) noexcept {
print_move_created(this, m.value); print_move_created(this, m.value);
std::swap(value, m.value); std::swap(value, m.value);
@ -66,8 +69,16 @@ public:
std::swap(value, m.value); std::swap(value, m.value);
return *this; return *this;
} }
MoveOrCopyInt(const MoveOrCopyInt &c) { print_copy_created(this, c.value); value = c.value; } MoveOrCopyInt(const MoveOrCopyInt &c) {
MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; } print_copy_created(this, c.value);
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
value = c.value;
}
MoveOrCopyInt &operator=(const MoveOrCopyInt &c) {
print_copy_assigned(this, c.value);
value = c.value;
return *this;
}
~MoveOrCopyInt() { print_destroyed(this); } ~MoveOrCopyInt() { print_destroyed(this); }
int value; int value;
@ -75,41 +86,71 @@ public:
class CopyOnlyInt { class CopyOnlyInt {
public: public:
CopyOnlyInt() { print_default_created(this); } CopyOnlyInt() { print_default_created(this); }
CopyOnlyInt(int v) : value{v} { print_created(this, value); } explicit CopyOnlyInt(int v) : value{v} { print_created(this, value); }
CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; } CopyOnlyInt(const CopyOnlyInt &c) {
CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; } print_copy_created(this, c.value);
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
value = c.value;
}
CopyOnlyInt &operator=(const CopyOnlyInt &c) {
print_copy_assigned(this, c.value);
value = c.value;
return *this;
}
~CopyOnlyInt() { print_destroyed(this); } ~CopyOnlyInt() { print_destroyed(this); }
int value; int value;
}; };
PYBIND11_NAMESPACE_BEGIN(pybind11) PYBIND11_NAMESPACE_BEGIN(pybind11)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <> struct type_caster<MoveOnlyInt> { template <>
PYBIND11_TYPE_CASTER(MoveOnlyInt, _x("MoveOnlyInt")); struct type_caster<MoveOnlyInt> {
bool load(handle src, bool) { value = MoveOnlyInt(src.cast<int>()); return true; } PYBIND11_TYPE_CASTER(MoveOnlyInt, const_name("MoveOnlyInt"));
static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } bool load(handle src, bool) {
value = MoveOnlyInt(src.cast<int>());
return true;
}
static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) {
return pybind11::cast(m.value, r, p);
}
}; };
template <> struct type_caster<MoveOrCopyInt> { template <>
PYBIND11_TYPE_CASTER(MoveOrCopyInt, _x("MoveOrCopyInt")); struct type_caster<MoveOrCopyInt> {
bool load(handle src, bool) { value = MoveOrCopyInt(src.cast<int>()); return true; } PYBIND11_TYPE_CASTER(MoveOrCopyInt, const_name("MoveOrCopyInt"));
static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } bool load(handle src, bool) {
value = MoveOrCopyInt(src.cast<int>());
return true;
}
static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) {
return pybind11::cast(m.value, r, p);
}
}; };
template <> struct type_caster<CopyOnlyInt> { template <>
struct type_caster<CopyOnlyInt> {
protected: protected:
CopyOnlyInt value; CopyOnlyInt value;
public: public:
static constexpr auto name = _x("CopyOnlyInt"); static constexpr auto name = const_name("CopyOnlyInt");
bool load(handle src, bool) { value = CopyOnlyInt(src.cast<int>()); return true; } bool load(handle src, bool) {
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } value = CopyOnlyInt(src.cast<int>());
return true;
}
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) {
return pybind11::cast(m.value, r, p);
}
static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) { static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) {
if (!src) return none().release(); if (!src) {
return none().release();
}
return cast(*src, policy, parent); return cast(*src, policy, parent);
} }
operator CopyOnlyInt*() { return &value; } explicit operator CopyOnlyInt *() { return &value; }
operator CopyOnlyInt&() { return value; } explicit operator CopyOnlyInt &() { return value; }
template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>; template <typename T>
using cast_op_type = pybind11::detail::cast_op_type<T>;
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(pybind11) PYBIND11_NAMESPACE_END(pybind11)
@ -117,23 +158,21 @@ PYBIND11_NAMESPACE_END(pybind11)
TEST_SUBMODULE(copy_move_policies, m) { TEST_SUBMODULE(copy_move_policies, m) {
// test_lacking_copy_ctor // test_lacking_copy_ctor
py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor") py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
.def_static("get_one", &lacking_copy_ctor::get_one, .def_static("get_one", &lacking_copy_ctor::get_one, py::return_value_policy::copy);
py::return_value_policy::copy);
// test_lacking_move_ctor // test_lacking_move_ctor
py::class_<lacking_move_ctor>(m, "lacking_move_ctor") py::class_<lacking_move_ctor>(m, "lacking_move_ctor")
.def_static("get_one", &lacking_move_ctor::get_one, .def_static("get_one", &lacking_move_ctor::get_one, py::return_value_policy::move);
py::return_value_policy::move);
// test_move_and_copy_casts // test_move_and_copy_casts
// NOLINTNEXTLINE(performance-unnecessary-value-param) // NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("move_and_copy_casts", [](const py::object &o) { m.def("move_and_copy_casts", [](const py::object &o) {
int r = 0; int r = 0;
r += py::cast<MoveOrCopyInt>(o).value; /* moves */ r += py::cast<MoveOrCopyInt>(o).value; /* moves */
r += py::cast<MoveOnlyInt>(o).value; /* moves */ r += py::cast<MoveOnlyInt>(o).value; /* moves */
r += py::cast<CopyOnlyInt>(o).value; /* copies */ r += py::cast<CopyOnlyInt>(o).value; /* copies */
auto m1(py::cast<MoveOrCopyInt>(o)); /* moves */ auto m1(py::cast<MoveOrCopyInt>(o)); /* moves */
auto m2(py::cast<MoveOnlyInt>(o)); /* moves */ auto m2(py::cast<MoveOnlyInt>(o)); /* moves */
auto m3(py::cast<CopyOnlyInt>(o)); /* copies */ auto m3(py::cast<CopyOnlyInt>(o)); /* copies */
r += m1.value + m2.value + m3.value; r += m1.value + m2.value + m3.value;
return r; return r;
@ -147,28 +186,34 @@ TEST_SUBMODULE(copy_move_policies, m) {
// Changing this breaks the existing test: needs careful review. // Changing this breaks the existing test: needs careful review.
// NOLINTNEXTLINE(performance-unnecessary-value-param) // NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("copy_only", [](CopyOnlyInt m) { return m.value; }); m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
m.def("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) { m.def("move_pair",
return p.first.value + p.second.value; [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) { return p.first.value + p.second.value; });
});
m.def("move_tuple", [](std::tuple<MoveOnlyInt, MoveOrCopyInt, MoveOnlyInt> t) { m.def("move_tuple", [](std::tuple<MoveOnlyInt, MoveOrCopyInt, MoveOnlyInt> t) {
return std::get<0>(t).value + std::get<1>(t).value + std::get<2>(t).value; return std::get<0>(t).value + std::get<1>(t).value + std::get<2>(t).value;
}); });
m.def("copy_tuple", [](std::tuple<CopyOnlyInt, CopyOnlyInt> t) { m.def("copy_tuple", [](std::tuple<CopyOnlyInt, CopyOnlyInt> t) {
return std::get<0>(t).value + std::get<1>(t).value; return std::get<0>(t).value + std::get<1>(t).value;
}); });
m.def("move_copy_nested", [](std::pair<MoveOnlyInt, std::pair<std::tuple<MoveOrCopyInt, CopyOnlyInt, std::tuple<MoveOnlyInt>>, MoveOrCopyInt>> x) { m.def("move_copy_nested",
return x.first.value + std::get<0>(x.second.first).value + std::get<1>(x.second.first).value + [](std::pair<MoveOnlyInt,
std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value; std::pair<std::tuple<MoveOrCopyInt, CopyOnlyInt, std::tuple<MoveOnlyInt>>,
}); MoveOrCopyInt>> x) {
return x.first.value + std::get<0>(x.second.first).value
+ std::get<1>(x.second.first).value
+ std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value;
});
m.def("move_and_copy_cstats", []() { m.def("move_and_copy_cstats", []() {
ConstructorStats::gc(); ConstructorStats::gc();
// Reset counts to 0 so that previous tests don't affect later ones: // Reset counts to 0 so that previous tests don't affect later ones:
auto &mc = ConstructorStats::get<MoveOrCopyInt>(); auto &mc = ConstructorStats::get<MoveOrCopyInt>();
mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions = 0; mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions
= 0;
auto &mo = ConstructorStats::get<MoveOnlyInt>(); auto &mo = ConstructorStats::get<MoveOnlyInt>();
mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions = 0; mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions
= 0;
auto &co = ConstructorStats::get<CopyOnlyInt>(); auto &co = ConstructorStats::get<CopyOnlyInt>();
co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions = 0; co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions
= 0;
py::dict d; py::dict d;
d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference); d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference);
d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference); d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference);
@ -178,18 +223,13 @@ TEST_SUBMODULE(copy_move_policies, m) {
#ifdef PYBIND11_HAS_OPTIONAL #ifdef PYBIND11_HAS_OPTIONAL
// test_move_and_copy_load_optional // test_move_and_copy_load_optional
m.attr("has_optional") = true; m.attr("has_optional") = true;
m.def("move_optional", [](std::optional<MoveOnlyInt> o) { m.def("move_optional", [](std::optional<MoveOnlyInt> o) { return o->value; });
return o->value; m.def("move_or_copy_optional", [](std::optional<MoveOrCopyInt> o) { return o->value; });
}); m.def("copy_optional", [](std::optional<CopyOnlyInt> o) { return o->value; });
m.def("move_or_copy_optional", [](std::optional<MoveOrCopyInt> o) { m.def("move_optional_tuple",
return o->value; [](std::optional<std::tuple<MoveOrCopyInt, MoveOnlyInt, CopyOnlyInt>> x) {
}); return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value;
m.def("copy_optional", [](std::optional<CopyOnlyInt> o) { });
return o->value;
});
m.def("move_optional_tuple", [](std::optional<std::tuple<MoveOrCopyInt, MoveOnlyInt, CopyOnlyInt>> x) {
return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value;
});
#else #else
m.attr("has_optional") = false; m.attr("has_optional") = false;
#endif #endif
@ -200,39 +240,53 @@ TEST_SUBMODULE(copy_move_policies, m) {
// added later. // added later.
struct PrivateOpNew { struct PrivateOpNew {
int value = 1; int value = 1;
private: private:
void *operator new(size_t bytes) { void *operator new(size_t bytes) {
void *ptr = std::malloc(bytes); void *ptr = std::malloc(bytes);
if (ptr) if (ptr) {
return ptr; return ptr;
}
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
}; };
py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value); py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
m.def("private_op_new_value", []() { return PrivateOpNew(); }); m.def("private_op_new_value", []() { return PrivateOpNew(); });
m.def("private_op_new_reference", []() -> const PrivateOpNew & { m.def(
static PrivateOpNew x{}; "private_op_new_reference",
return x; []() -> const PrivateOpNew & {
}, py::return_value_policy::reference); static PrivateOpNew x{};
return x;
},
py::return_value_policy::reference);
// test_move_fallback // test_move_fallback
// #389: rvp::move should fall-through to copy on non-movable objects // #389: rvp::move should fall-through to copy on non-movable objects
struct MoveIssue1 { struct MoveIssue1 {
int v; int v;
MoveIssue1(int v) : v{v} {} explicit MoveIssue1(int v) : v{v} {}
MoveIssue1(const MoveIssue1 &c) = default; MoveIssue1(const MoveIssue1 &c) = default;
MoveIssue1(MoveIssue1 &&) = delete; MoveIssue1(MoveIssue1 &&) = delete;
}; };
py::class_<MoveIssue1>(m, "MoveIssue1").def(py::init<int>()).def_readwrite("value", &MoveIssue1::v); py::class_<MoveIssue1>(m, "MoveIssue1")
.def(py::init<int>())
.def_readwrite("value", &MoveIssue1::v);
struct MoveIssue2 { struct MoveIssue2 {
int v; int v;
MoveIssue2(int v) : v{v} {} explicit MoveIssue2(int v) : v{v} {}
MoveIssue2(MoveIssue2 &&) = default; MoveIssue2(MoveIssue2 &&) = default;
}; };
py::class_<MoveIssue2>(m, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v); py::class_<MoveIssue2>(m, "MoveIssue2")
.def(py::init<int>())
.def_readwrite("value", &MoveIssue2::v);
// #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with `py::return_value_policy::move` // #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with
m.def("get_moveissue1", [](int i) { return std::unique_ptr<MoveIssue1>(new MoveIssue1(i)); }, py::return_value_policy::move); // `py::return_value_policy::move`
m.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move); m.def(
"get_moveissue1",
[](int i) { return std::unique_ptr<MoveIssue1>(new MoveIssue1(i)); },
py::return_value_policy::move);
m.def(
"get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
} }

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import copy_move_policies as m from pybind11_tests import copy_move_policies as m

Some files were not shown because too many files have changed in this diff Show More