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:
parent
e0802dcd26
commit
8ce38d3820
|
@ -7,13 +7,18 @@
|
|||
|
||||
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
|
||||
# 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})
|
||||
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()
|
||||
|
||||
# Extract project version from source
|
||||
|
@ -45,13 +50,8 @@ if(NOT pybind11_FIND_QUIETLY)
|
|||
message(STATUS "pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}")
|
||||
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
|
||||
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
|
||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
|
||||
set(lines
|
||||
|
@ -80,6 +80,8 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR AND NOT DEFINED PYBIND11_MASTER_
|
|||
endif()
|
||||
|
||||
set(pybind11_system "")
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
else()
|
||||
set(PYBIND11_MASTER_PROJECT OFF)
|
||||
set(pybind11_system SYSTEM)
|
||||
|
@ -89,6 +91,9 @@ endif()
|
|||
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
|
||||
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
|
||||
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(
|
||||
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
|
||||
cxx_right_angle_brackets)
|
||||
if(NOT "${PYBIND11_INTERNALS_VERSION}" STREQUAL "")
|
||||
target_compile_definitions(
|
||||
pybind11_headers INTERFACE "PYBIND11_INTERNALS_VERSION=${PYBIND11_INTERNALS_VERSION}")
|
||||
endif()
|
||||
else()
|
||||
# It is invalid to install a target twice, too.
|
||||
set(PYBIND11_INSTALL OFF)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
**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|
|
||||
|
||||
|
@ -13,19 +13,6 @@
|
|||
|
||||
.. 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
|
||||
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
|
||||
arguments.
|
||||
|
||||
- Python’s 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.
|
||||
|
||||
- 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
|
||||
equivalent bindings generated by Boost.Python. A recent pybind11
|
||||
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
|
||||
**5.8x**.
|
||||
|
||||
|
@ -147,7 +134,7 @@ About
|
|||
This project was created by `Wenzel
|
||||
Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or
|
||||
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,
|
||||
Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim
|
||||
Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart.
|
||||
|
@ -189,3 +176,5 @@ to the terms and conditions of this license.
|
|||
:target: https://repology.org/project/python:pybind11/versions
|
||||
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg
|
||||
: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
|
||||
|
|
|
@ -18,6 +18,5 @@ ALIASES += "endrst=\endverbatim"
|
|||
QUIET = YES
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \
|
||||
PY_MAJOR_VERSION=3 \
|
||||
PREDEFINED = PY_MAJOR_VERSION=3 \
|
||||
PYBIND11_NOINLINE
|
||||
|
|
|
@ -26,7 +26,9 @@ The following Python snippet demonstrates the intended usage from the Python sid
|
|||
def __int__(self):
|
||||
return 123
|
||||
|
||||
|
||||
from example import print
|
||||
|
||||
print(A())
|
||||
|
||||
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
|
||||
* '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
|
||||
|
|
|
@ -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
|
||||
``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:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
@ -203,7 +203,7 @@ adding the ``order='F'`` option when creating an array:
|
|||
|
||||
.. 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
|
||||
``Eigen::Ref<MatrixXd>`` (or similar column-major Eigen type).
|
||||
|
|
|
@ -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
|
||||
as arguments and return values, refer to the section on binding :ref:`classes`.
|
||||
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| Data type | Description | Header file |
|
||||
+====================================+===========================+===============================+
|
||||
+====================================+===========================+===================================+
|
||||
| ``int8_t``, ``uint8_t`` | 8-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` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| ``int64_t``, ``uint64_t`` | 64-bit integers | :file:`pybind11/pybind11.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| ``ssize_t``, ``size_t`` | Platform-dependent size | :file:`pybind11/pybind11.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| ``float``, ``double`` | Floating point types | :file:`pybind11/pybind11.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| ``bool`` | Two-state Boolean type | :file:`pybind11/pybind11.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| ``char`` | 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` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| ``wchar_t`` | Wide character 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 char32_t *`` | UTF-32 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::u16string`` | STL dynamic UTF-16 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::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` |
|
||||
| ``std::u16string_view``, etc. | | |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| ``std::pair<T1, T2>`` | Pair of two custom 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::complex<T>`` | Complex numbers | :file:`pybind11/complex.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| ``std::array<T, Size>`` | STL static 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::valarray<T>`` | STL value array | :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::unordered_map<T1, T2>`` | STL unordered map | :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::optional<T>`` | STL optional type (C++17) | :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::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::chrono::duration<...>`` | STL time duration | :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::Map<...>`` | Eigen: mapped memory | :file:`pybind11/eigen.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
| ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` |
|
||||
+------------------------------------+---------------------------+-------------------------------+
|
||||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
|
||||
.. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and
|
||||
``os.PathLike`` is converted to ``std::filesystem::path``, but this requires
|
||||
|
|
|
@ -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.
|
||||
🎂
|
||||
|
||||
>>> utf8_charptr('🍕')
|
||||
>>> utf8_charptr("🍕")
|
||||
My favorite food is
|
||||
🍕
|
||||
|
||||
|
@ -80,7 +80,7 @@ raise a ``UnicodeDecodeError``.
|
|||
}
|
||||
);
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> isinstance(example.std_string_return(), str)
|
||||
True
|
||||
|
@ -114,7 +114,7 @@ conversion has the same overhead as implicit conversion.
|
|||
}
|
||||
);
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> str_output()
|
||||
'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()
|
||||
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)
|
||||
True
|
||||
|
@ -229,16 +229,16 @@ character.
|
|||
m.def("pass_char", [](char c) { return c; });
|
||||
m.def("pass_wchar", [](wchar_t w) { return w; });
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> example.pass_char('A')
|
||||
>>> example.pass_char("A")
|
||||
'A'
|
||||
|
||||
While C++ will cast integers to character types (``char c = 0x65;``), pybind11
|
||||
does not convert Python integers to characters implicitly. The Python function
|
||||
``chr()`` can be used to convert integers to characters.
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> example.pass_char(0x65)
|
||||
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
|
||||
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 == "é"
|
||||
False
|
||||
|
||||
>>> example.pass_wchar(combining_e_acute)
|
||||
|
@ -278,9 +278,9 @@ single grapheme.
|
|||
Normalizing combining characters before passing the character literal to C++
|
||||
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
|
||||
|
|
|
@ -9,7 +9,7 @@ that you are already familiar with the basics from :doc:`/classes`.
|
|||
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
|
||||
given as a specific example of how one would do this with traditional C++
|
||||
code).
|
||||
|
@ -161,6 +161,7 @@ Here is an example:
|
|||
def __init__(self, name):
|
||||
Dog.__init__(self) # Without this, a TypeError is raised.
|
||||
self.name = name
|
||||
|
||||
def bark(self):
|
||||
return "yap!"
|
||||
|
||||
|
@ -1153,12 +1154,65 @@ error:
|
|||
|
||||
>>> class PyFinalChild(IsFinal):
|
||||
... pass
|
||||
...
|
||||
TypeError: type 'IsFinal' is not an acceptable base type
|
||||
|
||||
.. note:: This attribute is currently ignored on PyPy
|
||||
|
||||
.. 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
|
||||
============================
|
||||
|
||||
|
@ -1247,7 +1301,7 @@ Accessing the type object
|
|||
|
||||
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>();
|
||||
|
||||
|
@ -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`.
|
||||
|
||||
.. 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
|
||||
|
|
|
@ -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
|
||||
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
|
||||
shuts down and clears its memory. No Python functions can be called after this.
|
||||
|
||||
Executing Python code
|
||||
=====================
|
||||
|
||||
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
|
||||
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
|
||||
the context of an executable with an embedded interpreter:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
@ -108,7 +108,7 @@ The two approaches can also be combined:
|
|||
Importing modules
|
||||
=================
|
||||
|
||||
Python modules can be imported using `module_::import()`:
|
||||
Python modules can be imported using ``module_::import()``:
|
||||
|
||||
.. 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"""
|
||||
|
||||
|
||||
def add(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>();
|
||||
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
|
||||
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.
|
||||
|
@ -143,7 +144,7 @@ changes by the user. Note that this function does not reload modules recursively
|
|||
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
|
||||
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
|
||||
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
|
||||
imported in pure Python files loaded by the interpreter. Everything interacts
|
||||
|
@ -215,9 +216,9 @@ naturally:
|
|||
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
|
||||
`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.
|
||||
|
||||
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::
|
||||
|
||||
Creating two concurrent `scoped_interpreter` guards is a fatal error. So is
|
||||
calling `initialize_interpreter` for a second time after the interpreter
|
||||
Creating two concurrent ``scoped_interpreter`` guards is a fatal error. So is
|
||||
calling ``initialize_interpreter`` for a second time after the interpreter
|
||||
has already been initialized.
|
||||
|
||||
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
|
||||
=======================
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -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
|
||||
challenging and there are several caveats here, even within the pure
|
||||
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.
|
||||
|
|
|
@ -56,13 +56,15 @@ at its exception handler.
|
|||
+--------------------------------------+--------------------------------------+
|
||||
| :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`` |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
|
||||
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
|
||||
<handling_python_exceptions_cpp>` for further details.
|
||||
|
||||
|
@ -75,9 +77,10 @@ Registering custom translators
|
|||
|
||||
If the default exception conversion policy described above is insufficient,
|
||||
pybind11 also provides support for registering custom exception translators.
|
||||
To register a simple exception conversion that translates a C++ exception into
|
||||
a new Python exception using the C++ exception's ``what()`` method, a helper
|
||||
function is available:
|
||||
Similar to pybind11 classes, exception translators can be local to the module
|
||||
they are defined in or global to the entire python session. To register a simple
|
||||
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
|
||||
|
||||
|
@ -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``
|
||||
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
|
||||
parameter, a `handle`:
|
||||
parameter, a ``handle``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
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
|
||||
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
|
||||
``py::register_exception_translator(translator)`` can be used to register
|
||||
When more advanced exception translation is needed, the functions
|
||||
``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
|
||||
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
|
||||
signature ``void(std::exception_ptr)``.
|
||||
|
||||
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
|
||||
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
|
||||
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``.
|
||||
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 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
|
||||
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:
|
||||
|
||||
Handling unraisable exceptions
|
||||
|
|
|
@ -120,7 +120,7 @@ targeted arguments can be passed through the :class:`cpp_function` constructor:
|
|||
.. code-block:: cpp
|
||||
|
||||
class_<MyClass>(m, "MyClass")
|
||||
.def_property("data"
|
||||
.def_property("data",
|
||||
py::cpp_function(&MyClass::getData, py::return_value_policy::copy),
|
||||
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
|
||||
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`.
|
||||
|
||||
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
|
||||
|
||||
>>> print_dict({'foo': 123, 'bar': 'hello'})
|
||||
>>> print_dict({"foo": 123, "bar": "hello"})
|
||||
key=foo, value=123
|
||||
key=bar, value=hello
|
||||
|
||||
|
@ -306,8 +306,9 @@ The class ``py::args`` derives from ``py::tuple`` and ``py::kwargs`` derives
|
|||
from ``py::dict``.
|
||||
|
||||
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 accepted by the function.
|
||||
arguments. Note, however, that ``py::kwargs`` must always be the last argument
|
||||
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,
|
||||
and on how to cast their entries into C++ objects. A demonstration is also
|
||||
|
@ -366,6 +367,8 @@ like so:
|
|||
py::class_<MyClass>("MyClass")
|
||||
.def("myFunction", py::arg("arg") = static_cast<SomeType *>(nullptr));
|
||||
|
||||
.. _keyword_only_arguments:
|
||||
|
||||
Keyword-only arguments
|
||||
======================
|
||||
|
||||
|
@ -377,6 +380,7 @@ argument in a function definition:
|
|||
def f(a, *, b): # a can be positional or via keyword; b must be via keyword
|
||||
pass
|
||||
|
||||
|
||||
f(a=1, b=2) # good
|
||||
f(b=2, a=1) # good
|
||||
f(1, b=2) # good
|
||||
|
@ -396,6 +400,15 @@ feature does *not* require Python 3 to work.
|
|||
|
||||
.. 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
|
||||
=========================
|
||||
|
||||
|
@ -565,3 +578,38 @@ prefers earlier-defined overloads to later-defined ones.
|
|||
.. versionadded:: 2.6
|
||||
|
||||
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>);
|
||||
|
|
|
@ -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:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
|
|
@ -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
|
||||
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
|
||||
================
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
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()``).
|
||||
|
||||
.. seealso::
|
||||
|
@ -378,7 +403,7 @@ In Python 2, the syntactic sugar ``...`` is not available, but the singleton
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
a = # a NumPy array
|
||||
a = ... # a NumPy array
|
||||
b = a[0, ..., 0]
|
||||
|
||||
The function ``py::ellipsis()`` function can be used to perform the same
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
@ -30,7 +64,7 @@ types to Python, which can be done using :func:`py::cast`:
|
|||
|
||||
.. code-block:: cpp
|
||||
|
||||
MyClass *cls = ..;
|
||||
MyClass *cls = ...;
|
||||
py::object obj = py::cast(cls);
|
||||
|
||||
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):
|
||||
... # function code
|
||||
|
||||
|
||||
f(1234, say="hello", to=some_instance) # keyword call in Python
|
||||
|
||||
In C++, the same call can be made using:
|
||||
|
|
|
@ -28,7 +28,7 @@ Capturing standard output from ostream
|
|||
|
||||
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``
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
`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
|
||||
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>());
|
||||
|
||||
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
|
||||
|
||||
|
@ -103,7 +103,7 @@ arguments to disable one of the streams if needed.
|
|||
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
|
||||
can be used.
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ segmentation fault).
|
|||
.. code-block:: python
|
||||
|
||||
from example import Parent
|
||||
|
||||
print(Parent().get_child())
|
||||
|
||||
The problem is that ``Parent::get_child()`` returns a pointer to an instance of
|
||||
|
|
|
@ -109,7 +109,7 @@ a file named :file:`example.cpp` with the following contents:
|
|||
PYBIND11_MODULE(example, m) {
|
||||
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
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import random
|
||||
import os
|
||||
import time
|
||||
import datetime as dt
|
||||
import os
|
||||
import random
|
||||
|
||||
nfns = 4 # Functions per class
|
||||
nargs = 4 # Arguments per function
|
||||
|
|
|
@ -6,15 +6,460 @@ Changelog
|
|||
Starting with version 1.8.0, pybind11 releases use a `semantic versioning
|
||||
<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
|
||||
instead of applying globally across all pybind11 modules. Use
|
||||
``register_local_exception_translator(ExceptionTranslator&& translator)``
|
||||
instead of ``register_exception_translator(ExceptionTranslator&&
|
||||
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)
|
||||
---------------------
|
||||
|
@ -87,8 +532,8 @@ New features:
|
|||
|
||||
Changes:
|
||||
|
||||
* ``py::str`` changed to exclusively hold `PyUnicodeObject`. Previously
|
||||
``py::str`` could also hold `bytes`, which is probably surprising, was
|
||||
* ``py::str`` changed to exclusively hold ``PyUnicodeObject``. Previously
|
||||
``py::str`` could also hold ``bytes``, which is probably surprising, was
|
||||
never documented, and can mask bugs (e.g. accidental use of ``py::str``
|
||||
instead of ``py::bytes``).
|
||||
`#2409 <https://github.com/pybind/pybind11/pull/2409>`_
|
||||
|
@ -1028,6 +1473,7 @@ v2.2.0 (August 31, 2017)
|
|||
|
||||
from cpp_module import CppBase1, CppBase2
|
||||
|
||||
|
||||
class PyDerived(CppBase1, CppBase2):
|
||||
def __init__(self):
|
||||
CppBase1.__init__(self) # C++ bases must be initialized explicitly
|
||||
|
@ -1240,7 +1686,7 @@ v2.2.0 (August 31, 2017)
|
|||
* Intel C++ compiler compatibility fixes.
|
||||
`#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>`_.
|
||||
|
||||
* Added ``py::hash`` to fetch the hash value of Python objects, and
|
||||
|
|
|
@ -44,12 +44,12 @@ interactive Python session demonstrating this example is shown below:
|
|||
|
||||
% python
|
||||
>>> import example
|
||||
>>> p = example.Pet('Molly')
|
||||
>>> p = example.Pet("Molly")
|
||||
>>> print(p)
|
||||
<example.Pet object at 0x10cd98060>
|
||||
>>> p.getName()
|
||||
u'Molly'
|
||||
>>> p.setName('Charly')
|
||||
>>> p.setName("Charly")
|
||||
>>> p.getName()
|
||||
u'Charly'
|
||||
|
||||
|
@ -122,10 +122,10 @@ This makes it possible to write
|
|||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = example.Pet('Molly')
|
||||
>>> p = example.Pet("Molly")
|
||||
>>> p.name
|
||||
u'Molly'
|
||||
>>> p.name = 'Charly'
|
||||
>>> p.name = "Charly"
|
||||
>>> p.name
|
||||
u'Charly'
|
||||
|
||||
|
@ -174,10 +174,10 @@ Native Python classes can pick up new attributes dynamically:
|
|||
.. code-block:: pycon
|
||||
|
||||
>>> class Pet:
|
||||
... name = 'Molly'
|
||||
... name = "Molly"
|
||||
...
|
||||
>>> p = Pet()
|
||||
>>> p.name = 'Charly' # overwrite existing
|
||||
>>> p.name = "Charly" # overwrite existing
|
||||
>>> p.age = 2 # dynamically add a new attribute
|
||||
|
||||
By default, classes exported from C++ do not support this and the only writable
|
||||
|
@ -195,7 +195,7 @@ Trying to set any other attribute results in an error:
|
|||
.. code-block:: pycon
|
||||
|
||||
>>> p = example.Pet()
|
||||
>>> p.name = 'Charly' # OK, attribute defined in C++
|
||||
>>> p.name = "Charly" # OK, attribute defined in C++
|
||||
>>> p.age = 2 # fail
|
||||
AttributeError: 'Pet' object has no attribute 'age'
|
||||
|
||||
|
@ -213,7 +213,7 @@ Now everything works as expected:
|
|||
.. code-block:: pycon
|
||||
|
||||
>>> 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.__dict__ # just like a native Python class
|
||||
{'age': 2}
|
||||
|
@ -280,7 +280,7 @@ expose fields and methods of both types:
|
|||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = example.Dog('Molly')
|
||||
>>> p = example.Dog("Molly")
|
||||
>>> p.name
|
||||
u'Molly'
|
||||
>>> p.bark()
|
||||
|
@ -446,8 +446,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth
|
|||
Enumerations and internal types
|
||||
===============================
|
||||
|
||||
Let's now suppose that the example class contains an internal enumeration type,
|
||||
e.g.:
|
||||
Let's now suppose that the example class contains internal types like enumerations, e.g.:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
|
@ -457,10 +456,15 @@ e.g.:
|
|||
Cat
|
||||
};
|
||||
|
||||
struct Attributes {
|
||||
float age = 0;
|
||||
};
|
||||
|
||||
Pet(const std::string &name, Kind type) : name(name), type(type) { }
|
||||
|
||||
std::string name;
|
||||
Kind type;
|
||||
Attributes attr;
|
||||
};
|
||||
|
||||
The binding code for this example looks as follows:
|
||||
|
@ -471,22 +475,28 @@ The binding code for this example looks as follows:
|
|||
|
||||
pet.def(py::init<const std::string &, Pet::Kind>())
|
||||
.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")
|
||||
.value("Dog", Pet::Kind::Dog)
|
||||
.value("Cat", Pet::Kind::Cat)
|
||||
.export_values();
|
||||
|
||||
To ensure that the ``Kind`` type is created within the scope of ``Pet``, the
|
||||
``pet`` :class:`class_` instance must be supplied to the :class:`enum_`.
|
||||
py::class_<Pet::Attributes> attributes(pet, "Attributes")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("age", &Pet::Attributes::age);
|
||||
|
||||
|
||||
To ensure that the nested types ``Kind`` and ``Attributes`` are created within the scope of ``Pet``, the
|
||||
``pet`` :class:`class_` instance must be supplied to the :class:`enum_` and :class:`class_`
|
||||
constructor. The :func:`enum_::export_values` function exports the enum entries
|
||||
into the parent scope, which should be skipped for newer C++11-style strongly
|
||||
typed enums.
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> p = Pet('Lucy', Pet.Cat)
|
||||
>>> p = Pet("Lucy", Pet.Cat)
|
||||
>>> p.type
|
||||
Kind.Cat
|
||||
>>> int(p.type)
|
||||
|
|
|
@ -42,10 +42,7 @@ An example of a ``setup.py`` using pybind11's helpers:
|
|||
),
|
||||
]
|
||||
|
||||
setup(
|
||||
...,
|
||||
ext_modules=ext_modules
|
||||
)
|
||||
setup(..., ext_modules=ext_modules)
|
||||
|
||||
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
|
||||
|
@ -64,11 +61,7 @@ that is supported via a ``build_ext`` command override; it will only affect
|
|||
),
|
||||
]
|
||||
|
||||
setup(
|
||||
...,
|
||||
cmdclass={"build_ext": build_ext},
|
||||
ext_modules=ext_modules
|
||||
)
|
||||
setup(..., cmdclass={"build_ext": build_ext}, ext_modules=ext_modules)
|
||||
|
||||
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``
|
||||
|
@ -113,7 +106,7 @@ with the following:
|
|||
|
||||
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
|
||||
|
@ -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_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 ``-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``
|
||||
|
||||
``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``
|
||||
An alternative to `INTERPROCEDURAL_OPTIMIZATION` for adding link-time optimization.
|
||||
|
|
|
@ -13,12 +13,11 @@
|
|||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
DIR = Path(__file__).parent.resolve()
|
||||
|
||||
|
|
|
@ -63,6 +63,9 @@ Convenience functions converting to Python types
|
|||
.. doxygenfunction:: make_key_iterator(Iterator, Sentinel, Extra &&...)
|
||||
.. doxygenfunction:: make_key_iterator(Type &, Extra&&...)
|
||||
|
||||
.. doxygenfunction:: make_value_iterator(Iterator, Sentinel, Extra &&...)
|
||||
.. doxygenfunction:: make_value_iterator(Type &, Extra&&...)
|
||||
|
||||
.. _extras:
|
||||
|
||||
Passing extra arguments to ``def`` or ``class_``
|
||||
|
|
|
@ -22,11 +22,15 @@ the version just below.
|
|||
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 ``PYBIND11_VERSION_MAJOR`` etc. in
|
||||
``include/pybind11/detail/common.h``. PATCH should be a simple integer.
|
||||
- Update the version HEX just below, as well.
|
||||
- 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
|
||||
supported Python versions.
|
||||
- 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
|
||||
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
|
||||
for you, so you could skip the above step).
|
||||
- GUI method: click "Create a new release" on the far right, fill in the tag
|
||||
name (if you didn't tag above, it will be made here), fill in a release
|
||||
name like "Version X.Y.Z", and optionally copy-and-paste the changelog into
|
||||
the description (processed as markdown by Pandoc). Check "pre-release" if
|
||||
this is a beta/RC. You can get partway there with
|
||||
``cat docs/changelog.rst | pandoc -f rst -t gfm``.
|
||||
for you, so you could skip the above step.)
|
||||
- GUI method: Under `releases <https://github.com/pybind/pybind11/releases>`_
|
||||
click "Draft a new release" on the far right, fill in the tag name
|
||||
(if you didn't tag above, it will be made here), fill in a release name
|
||||
like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog
|
||||
into the description (usually ``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"``
|
||||
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
|
||||
``0.dev1`` and increment MINOR).
|
||||
- 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``.
|
||||
- ``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:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: console
|
||||
|
||||
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
|
||||
|
||||
python3 -m pip install build
|
||||
python3 -m build
|
||||
PYBIND11_SDIST_GLOBAL=1 python3 -m build
|
||||
nox -s build
|
||||
twine upload dist/*
|
||||
|
||||
This makes SDists and wheels, and the final line uploads them.
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
breathe==4.26.1
|
||||
# docutils 0.17 breaks HTML tags & RTD theme
|
||||
# https://github.com/sphinx-doc/sphinx/issues/9001
|
||||
docutils==0.16
|
||||
sphinx==3.3.1
|
||||
sphinx_rtd_theme==0.5.0
|
||||
sphinxcontrib-moderncmakedomain==3.17
|
||||
sphinxcontrib-svg2pdfconverter==1.1.0
|
||||
breathe==4.31.0
|
||||
sphinx==3.5.4
|
||||
sphinx_rtd_theme==1.0.0
|
||||
sphinxcontrib-moderncmakedomain==3.19
|
||||
sphinxcontrib-svg2pdfconverter==1.1.1
|
||||
|
|
|
@ -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
|
||||
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
|
||||
====
|
||||
|
@ -34,6 +48,7 @@ to be common:
|
|||
careful review and custom fixes.
|
||||
|
||||
|
||||
.. _upgrade-guide-2.6:
|
||||
|
||||
v2.6
|
||||
====
|
||||
|
|
|
@ -12,13 +12,18 @@
|
|||
|
||||
#include "cast.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
/// \addtogroup annotations
|
||||
/// @{
|
||||
|
||||
/// 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
|
||||
struct is_operator {};
|
||||
|
@ -27,26 +32,41 @@ struct is_operator { };
|
|||
struct is_final {};
|
||||
|
||||
/// 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
|
||||
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
|
||||
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"
|
||||
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
|
||||
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<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
|
||||
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
|
||||
struct multiple_inheritance {};
|
||||
|
@ -69,8 +89,28 @@ struct metaclass {
|
|||
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:
|
||||
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
|
||||
struct arithmetic {};
|
||||
|
@ -96,9 +136,13 @@ struct prepend { };
|
|||
return foo(args...); // forwarded arguments
|
||||
});
|
||||
\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>
|
||||
struct call_guard<T> {
|
||||
|
@ -123,8 +167,9 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
|||
enum op_id : int;
|
||||
enum op_type : int;
|
||||
struct undefined_t;
|
||||
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
|
||||
inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
|
||||
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t>
|
||||
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
|
||||
struct argument_record {
|
||||
|
@ -138,12 +183,13 @@ struct argument_record {
|
|||
: 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 {
|
||||
function_record()
|
||||
: is_constructor(false), is_new_style_constructor(false), is_stateless(false),
|
||||
is_operator(false), is_method(false), has_args(false),
|
||||
has_kwargs(false), has_kw_only_args(false), prepend(false) { }
|
||||
is_operator(false), is_method(false), has_args(false), has_kwargs(false),
|
||||
prepend(false) {}
|
||||
|
||||
/// Function name
|
||||
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
|
||||
|
@ -190,17 +236,15 @@ struct function_record {
|
|||
/// True if the function has a '**kwargs' argument
|
||||
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
|
||||
bool prepend : 1;
|
||||
|
||||
/// Number of arguments (including py::args and/or py::kwargs, if present)
|
||||
std::uint16_t nargs;
|
||||
|
||||
/// Number of trailing arguments (counted in `nargs`) that are keyword-only
|
||||
std::uint16_t nargs_kw_only = 0;
|
||||
/// Number of leading positional arguments, which are terminated by a py::args or py::kwargs
|
||||
/// 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
|
||||
std::uint16_t nargs_pos_only = 0;
|
||||
|
@ -260,6 +304,9 @@ struct type_record {
|
|||
/// Custom metaclass (optional)
|
||||
handle metaclass;
|
||||
|
||||
/// Custom type setup.
|
||||
custom_type_setup::callback custom_type_setup_callback;
|
||||
|
||||
/// Multiple inheritance marker
|
||||
bool multiple_inheritance : 1;
|
||||
|
||||
|
@ -279,35 +326,36 @@ struct type_record {
|
|||
bool is_final : 1;
|
||||
|
||||
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) {
|
||||
std::string tname(base.name());
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("generic_type: type \"" + std::string(name) +
|
||||
"\" referenced unknown base type \"" + tname + "\"");
|
||||
pybind11_fail("generic_type: type \"" + std::string(name)
|
||||
+ "\" referenced unknown base type \"" + tname + "\"");
|
||||
}
|
||||
|
||||
if (default_holder != base_info->default_holder) {
|
||||
std::string tname(base.name());
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("generic_type: type \"" + std::string(name) + "\" " +
|
||||
(default_holder ? "does not have" : "has") +
|
||||
" a non-default holder type while its base \"" + tname + "\" " +
|
||||
(base_info->default_holder ? "does not" : "does"));
|
||||
pybind11_fail("generic_type: type \"" + std::string(name) + "\" "
|
||||
+ (default_holder ? "does not have" : "has")
|
||||
+ " a non-default holder type while its base \"" + tname + "\" "
|
||||
+ (base_info->default_holder ? "does not" : "does"));
|
||||
}
|
||||
|
||||
bases.append((PyObject *) base_info->type);
|
||||
|
||||
if (base_info->type->tp_dictoffset != 0)
|
||||
if (base_info->type->tp_dictoffset != 0) {
|
||||
dynamic_attr = true;
|
||||
}
|
||||
|
||||
if (caster)
|
||||
if (caster) {
|
||||
base_info->implicit_casts.emplace_back(type, caster);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline function_call::function_call(const function_record &f, handle p) :
|
||||
func(f), parent(p) {
|
||||
inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) {
|
||||
args.reserve(f.nargs);
|
||||
args_convert.reserve(f.nargs);
|
||||
}
|
||||
|
@ -321,9 +369,11 @@ struct is_new_style_constructor { };
|
|||
* fields in the type_record and function_record data structures or executed at
|
||||
* 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
|
||||
static void init(const T &, function_record *) {}
|
||||
static void init(const T &, type_record *) {}
|
||||
|
@ -332,89 +382,120 @@ template <typename T> struct process_attribute_default {
|
|||
};
|
||||
|
||||
/// 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); }
|
||||
};
|
||||
|
||||
/// 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); }
|
||||
};
|
||||
|
||||
/// 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, 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
|
||||
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; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates that this is an overloaded function associated with a given sibling
|
||||
template <> struct process_attribute<sibling> : process_attribute_default<sibling> {
|
||||
/// Process an attribute which indicates that this is an overloaded function associated with a
|
||||
/// given sibling
|
||||
template <>
|
||||
struct process_attribute<sibling> : process_attribute_default<sibling> {
|
||||
static void init(const sibling &s, function_record *r) { r->sibling = s.value; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates that this function is a method
|
||||
template <> 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_; }
|
||||
template <>
|
||||
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
|
||||
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; }
|
||||
};
|
||||
|
||||
/// 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; }
|
||||
};
|
||||
|
||||
template <> 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; }
|
||||
template <>
|
||||
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) {
|
||||
if (!a.name || a.name[0] == '\0')
|
||||
pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation");
|
||||
++r->nargs_kw_only;
|
||||
inline void check_kw_only_arg(const arg &a, function_record *r) {
|
||||
if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) {
|
||||
pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or "
|
||||
"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)
|
||||
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) {
|
||||
if (r->is_method && r->args.empty())
|
||||
r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/);
|
||||
append_self_arg_if_needed(r);
|
||||
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)
|
||||
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) {
|
||||
if (r->is_method && r->args.empty())
|
||||
r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/);
|
||||
if (r->is_method && r->args.empty()) {
|
||||
r->args.emplace_back(
|
||||
"self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false);
|
||||
}
|
||||
|
||||
if (!a.value) {
|
||||
#if !defined(NDEBUG)
|
||||
std::string descr("'");
|
||||
if (a.name) descr += std::string(a.name) + ": ";
|
||||
if (a.name) {
|
||||
descr += std::string(a.name) + ": ";
|
||||
}
|
||||
descr += a.type + "'";
|
||||
if (r->is_method) {
|
||||
if (r->name)
|
||||
descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'";
|
||||
else
|
||||
if (r->name) {
|
||||
descr += " in method '" + (std::string) str(r->scope) + "."
|
||||
+ (std::string) r->name + "'";
|
||||
} else {
|
||||
descr += " in method of '" + (std::string) str(r->scope) + "'";
|
||||
}
|
||||
} else if (r->name) {
|
||||
descr += " in function '" + (std::string) r->name + "'";
|
||||
}
|
||||
pybind11_fail("arg(): could not convert default argument "
|
||||
+ descr + " into a Python object (type not registered yet?)");
|
||||
pybind11_fail("arg(): could not convert default argument " + descr
|
||||
+ " into a Python object (type not registered yet?)");
|
||||
#else
|
||||
pybind11_fail("arg(): could not convert default argument "
|
||||
"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);
|
||||
|
||||
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
|
||||
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) {
|
||||
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
|
||||
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) {
|
||||
append_self_arg_if_needed(r);
|
||||
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>
|
||||
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); }
|
||||
};
|
||||
|
||||
|
@ -456,7 +551,9 @@ struct process_attribute<base<T>> : process_attribute_default<base<T>> {
|
|||
/// Process a multiple inheritance attribute
|
||||
template <>
|
||||
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 <>
|
||||
|
@ -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; }
|
||||
};
|
||||
|
||||
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 <>
|
||||
struct process_attribute<is_final> : process_attribute_default<is_final> {
|
||||
static void init(const is_final &, type_record *r) { r->is_final = true; }
|
||||
|
@ -502,34 +606,52 @@ struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_gua
|
|||
* pre-call handler if both Nurse, Patient != 0 and use the post-call handler
|
||||
* 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>
|
||||
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>
|
||||
static void postcall(function_call &, handle) {}
|
||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
|
||||
static void precall(function_call &) {}
|
||||
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
|
||||
template <typename... Args> struct process_attributes {
|
||||
template <typename... Args>
|
||||
struct process_attributes {
|
||||
static void init(const Args &...args, function_record *r) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
|
||||
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) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
|
||||
ignore_unused(unused);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||
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) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::precall(call), 0) ... };
|
||||
ignore_unused(unused);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call);
|
||||
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) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0) ... };
|
||||
ignore_unused(unused);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret);
|
||||
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)...};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -545,6 +667,7 @@ template <typename... Extra,
|
|||
size_t named = constexpr_sum(std::is_base_of<arg, 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) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs);
|
||||
return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,11 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
|||
inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
|
||||
auto ndim = shape.size();
|
||||
std::vector<ssize_t> strides(ndim, itemsize);
|
||||
if (ndim > 0)
|
||||
for (size_t i = ndim - 1; i > 0; --i)
|
||||
if (ndim > 0) {
|
||||
for (size_t i = ndim - 1; i > 0; --i) {
|
||||
strides[i - 1] = strides[i] * shape[i];
|
||||
}
|
||||
}
|
||||
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) {
|
||||
auto ndim = shape.size();
|
||||
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];
|
||||
}
|
||||
return strides;
|
||||
}
|
||||
|
||||
|
@ -41,29 +44,52 @@ struct buffer_info {
|
|||
void *ptr = nullptr; // Pointer to the underlying storage
|
||||
ssize_t itemsize = 0; // Size of individual items in bytes
|
||||
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
|
||||
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
|
||||
|
||||
buffer_info() = default;
|
||||
|
||||
buffer_info(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=false)
|
||||
buffer_info(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 = 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())
|
||||
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
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(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(T *ptr,
|
||||
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,
|
||||
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>
|
||||
|
@ -72,10 +98,15 @@ struct buffer_info {
|
|||
|
||||
template <typename T>
|
||||
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)
|
||||
: 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},
|
||||
/* Though buffer::request() requests PyBUF_STRIDES, ctypes objects
|
||||
* ignore this flag and return a view with NULL strides.
|
||||
|
@ -84,7 +115,9 @@ struct buffer_info {
|
|||
? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
|
||||
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
|
||||
(view->readonly != 0)) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
this->m_view = view;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
this->ownview = ownview;
|
||||
}
|
||||
|
||||
|
@ -108,17 +141,28 @@ struct 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() { 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,
|
||||
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) { }
|
||||
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;
|
||||
bool ownview = false;
|
||||
|
@ -126,17 +170,22 @@ private:
|
|||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T, typename SFINAE = void> struct compare_buffer_info {
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct compare_buffer_info {
|
||||
static bool compare(const buffer_info &b) {
|
||||
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>
|
||||
struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
|
||||
static bool compare(const buffer_info &b) {
|
||||
return (size_t) b.itemsize == sizeof(T) && (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")));
|
||||
return (size_t) b.itemsize == sizeof(T)
|
||||
&& (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
|
@ -15,11 +15,8 @@
|
|||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <datetime.h>
|
||||
#include <mutex>
|
||||
|
||||
// Backport the PyDateTime_DELTA functions from Python3.3 if required
|
||||
#ifndef PyDateTime_DELTA_GET_DAYS
|
||||
|
@ -35,20 +32,26 @@
|
|||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename type> class duration_caster {
|
||||
template <typename type>
|
||||
class duration_caster {
|
||||
public:
|
||||
using rep = typename type::rep;
|
||||
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) {
|
||||
using namespace std::chrono;
|
||||
|
||||
// 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 (PyDelta_Check(src.ptr())) {
|
||||
value = type(duration_cast<duration<rep, period>>(
|
||||
|
@ -59,19 +62,23 @@ public:
|
|||
}
|
||||
// If invoked with a float we assume it is seconds and convert
|
||||
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 false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
@ -83,9 +90,12 @@ public:
|
|||
auto d = get_duration(src);
|
||||
|
||||
// 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 ss_t = duration<int, std::ratio<1>>;
|
||||
using us_t = duration<int, std::micro>;
|
||||
|
@ -97,7 +107,7 @@ public:
|
|||
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) {
|
||||
|
@ -108,7 +118,7 @@ inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
|
|||
#else
|
||||
static std::mutex 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) {
|
||||
*buf = *tm_ptr;
|
||||
}
|
||||
|
@ -117,16 +127,21 @@ 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
|
||||
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:
|
||||
using type = std::chrono::time_point<std::chrono::system_clock, Duration>;
|
||||
bool load(handle src, bool) {
|
||||
using namespace std::chrono;
|
||||
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
if (!PyDateTimeAPI) {
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!src) return false;
|
||||
if (!src) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::tm cal;
|
||||
microseconds msecs;
|
||||
|
@ -158,35 +173,43 @@ public:
|
|||
cal.tm_year = 70; // earliest available date for Python's datetime
|
||||
cal.tm_isdst = -1;
|
||||
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);
|
||||
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;
|
||||
|
||||
// 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
|
||||
// (cfr. https://github.com/pybind/pybind11/issues/2417)
|
||||
// Get out microseconds, and make sure they are positive, to avoid bug in eastern
|
||||
// hemisphere time zones (cfr. https://github.com/pybind/pybind11/issues/2417)
|
||||
using us_t = duration<int, std::micro>;
|
||||
auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1));
|
||||
if (us.count() < 0)
|
||||
if (us.count() < 0) {
|
||||
us += seconds(1);
|
||||
}
|
||||
|
||||
// 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.
|
||||
// (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));
|
||||
// > If std::time_t has lower precision, it is implementation-defined whether the value is
|
||||
// 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::tm 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");
|
||||
}
|
||||
return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
|
||||
localtime.tm_mon + 1,
|
||||
localtime.tm_mday,
|
||||
|
@ -195,19 +218,19 @@ public:
|
|||
localtime.tm_sec,
|
||||
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
|
||||
// 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
|
||||
template <typename Clock, typename Duration> class type_caster<std::chrono::time_point<Clock, Duration>>
|
||||
: public duration_caster<std::chrono::time_point<Clock, Duration>> {
|
||||
};
|
||||
template <typename Clock, typename 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>>
|
||||
: public duration_caster<std::chrono::duration<Rep, Period>> {
|
||||
};
|
||||
template <typename Rep, typename Period>
|
||||
class type_caster<std::chrono::duration<Rep, Period>>
|
||||
: public duration_caster<std::chrono::duration<Rep, Period>> {};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <complex>
|
||||
|
||||
/// glibc defines I as a macro which breaks things, e.g., boost template names
|
||||
|
@ -19,7 +20,8 @@
|
|||
|
||||
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 value[3] = {'Z', c, '\0'};
|
||||
static std::string format() { return std::string(value); }
|
||||
|
@ -27,25 +29,31 @@ template <typename T> struct format_descriptor<std::complex<T>, detail::enable_i
|
|||
|
||||
#ifndef PYBIND11_CPP17
|
||||
|
||||
template <typename T> constexpr const char format_descriptor<
|
||||
std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
|
||||
template <typename T>
|
||||
constexpr const char
|
||||
format_descriptor<std::complex<T>,
|
||||
detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
|
||||
|
||||
#endif
|
||||
|
||||
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 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:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!src)
|
||||
if (!src) {
|
||||
return false;
|
||||
if (!convert && !PyComplex_Check(src.ptr()))
|
||||
}
|
||||
if (!convert && !PyComplex_Check(src.ptr())) {
|
||||
return false;
|
||||
}
|
||||
Py_complex result = PyComplex_AsCComplex(src.ptr());
|
||||
if (result.real == -1.0 && PyErr_Occurred()) {
|
||||
PyErr_Clear();
|
||||
|
@ -55,11 +63,12 @@ public:
|
|||
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());
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(std::complex<T>, _x("complex"));
|
||||
PYBIND11_TYPE_CASTER(std::complex<T>, const_name("complex"));
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -21,7 +21,8 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
|||
#else
|
||||
// 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:
|
||||
# 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
|
||||
|
||||
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
|
||||
garbage collector (the GC will call type_traverse(), which will in
|
||||
turn find the newly constructed type in an invalid state) */
|
||||
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
|
||||
if (!heap_type)
|
||||
auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
|
||||
if (!heap_type) {
|
||||
pybind11_fail("make_static_property_type(): error allocating type!");
|
||||
}
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
# ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
# endif
|
||||
|
||||
auto type = &heap_type->ht_type;
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_name = name;
|
||||
type->tp_base = type_incref(&PyProperty_Type);
|
||||
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||
type->tp_descr_get = pybind11_static_get;
|
||||
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()!");
|
||||
}
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
|
||||
|
@ -105,8 +108,10 @@ inline PyTypeObject *make_static_property_type() {
|
|||
def __set__(self, obj, value):
|
||||
cls = obj if isinstance(obj, type) else type(obj)
|
||||
property.__set__(self, cls, value)
|
||||
)", Py_file_input, d.ptr(), d.ptr()
|
||||
);
|
||||
)",
|
||||
Py_file_input,
|
||||
d.ptr(),
|
||||
d.ptr());
|
||||
if (result == nullptr)
|
||||
throw error_already_set();
|
||||
Py_DECREF(result);
|
||||
|
@ -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)`
|
||||
// 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop`
|
||||
// 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)
|
||||
&& (PyObject_IsInstance(descr, 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
|
||||
auto instance = reinterpret_cast<detail::instance *>(self);
|
||||
auto *instance = reinterpret_cast<detail::instance *>(self);
|
||||
|
||||
// Ensure that the base __init__ function(s) were called
|
||||
for (const auto &vh : values_and_holders(instance)) {
|
||||
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());
|
||||
Py_DECREF(self);
|
||||
return nullptr;
|
||||
|
@ -201,28 +207,29 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
|||
// 1) be found in internals.registered_types_py
|
||||
// 2) have exactly one associated `detail::type_info`
|
||||
auto found_type = internals.registered_types_py.find(type);
|
||||
if (found_type != internals.registered_types_py.end() &&
|
||||
found_type->second.size() == 1 &&
|
||||
found_type->second[0]->type == type) {
|
||||
if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1
|
||||
&& found_type->second[0]->type == type) {
|
||||
|
||||
auto *tinfo = found_type->second[0];
|
||||
auto tindex = std::type_index(*tinfo->cpptype);
|
||||
internals.direct_conversions.erase(tindex);
|
||||
|
||||
if (tinfo->module_local)
|
||||
registered_local_types_cpp().erase(tindex);
|
||||
else
|
||||
if (tinfo->module_local) {
|
||||
get_local_internals().registered_types_cpp.erase(tindex);
|
||||
} else {
|
||||
internals.registered_types_cpp.erase(tindex);
|
||||
}
|
||||
internals.registered_types_py.erase(tinfo->type);
|
||||
|
||||
// Actually just `std::erase_if`, but that's only available in C++20
|
||||
auto &cache = internals.inactive_override_cache;
|
||||
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);
|
||||
else
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
delete tinfo;
|
||||
}
|
||||
|
@ -241,16 +248,17 @@ inline PyTypeObject* make_default_metaclass() {
|
|||
issue no Python C API calls which could potentially invoke the
|
||||
garbage collector (the GC will call type_traverse(), which will in
|
||||
turn find the newly constructed type in an invalid state) */
|
||||
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
|
||||
if (!heap_type)
|
||||
auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
|
||||
if (!heap_type) {
|
||||
pybind11_fail("make_default_metaclass(): error allocating metaclass!");
|
||||
}
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
auto type = &heap_type->ht_type;
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_name = name;
|
||||
type->tp_base = type_incref(&PyType_Type);
|
||||
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;
|
||||
|
||||
if (PyType_Ready(type) < 0)
|
||||
if (PyType_Ready(type) < 0) {
|
||||
pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!");
|
||||
}
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
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
|
||||
/// 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.
|
||||
inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self,
|
||||
/// correctly recognize an offset base class pointer. This calls a function with any offset base
|
||||
/// ptrs.
|
||||
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)) {
|
||||
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) {
|
||||
if (c.first == tinfo->cpptype) {
|
||||
auto *parentptr = c.second(valueptr);
|
||||
if (parentptr != valueptr)
|
||||
if (parentptr != valueptr) {
|
||||
f(parentptr, self);
|
||||
}
|
||||
traverse_offset_bases(parentptr, parent_tinfo, self, f);
|
||||
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) {
|
||||
register_instance_impl(valptr, self);
|
||||
if (!tinfo->simple_ancestors)
|
||||
if (!tinfo->simple_ancestors) {
|
||||
traverse_offset_bases(valptr, tinfo, self, register_instance_impl);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) {
|
||||
bool ret = deregister_instance_impl(valptr, self);
|
||||
if (!tinfo->simple_ancestors)
|
||||
if (!tinfo->simple_ancestors) {
|
||||
traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Instance creation function for all pybind11 types. It allocates the internal instance layout for
|
||||
/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast
|
||||
/// to a reference or pointer), and initialization is done by an `__init__` function.
|
||||
/// Instance creation function for all pybind11 types. It allocates the internal instance layout
|
||||
/// for holding C++ objects and holders. Allocation is done lazily (the first time the instance is
|
||||
/// cast to a reference or pointer), and initialization is done by an `__init__` function.
|
||||
inline PyObject *make_new_instance(PyTypeObject *type) {
|
||||
#if defined(PYPY_VERSION)
|
||||
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
|
||||
// object is a plain Python type (i.e. not derived from an extension type). Fix it.
|
||||
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first
|
||||
// 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));
|
||||
if (type->tp_basicsize < instance_size) {
|
||||
type->tp_basicsize = instance_size;
|
||||
}
|
||||
#endif
|
||||
PyObject *self = type->tp_alloc(type, 0);
|
||||
auto inst = reinterpret_cast<instance *>(self);
|
||||
auto *inst = reinterpret_cast<instance *>(self);
|
||||
// Allocate the value/holder internals:
|
||||
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) {
|
||||
auto &internals = get_internals();
|
||||
auto instance = reinterpret_cast<detail::instance *>(nurse);
|
||||
auto *instance = reinterpret_cast<detail::instance *>(nurse);
|
||||
instance->has_patients = true;
|
||||
Py_INCREF(patient);
|
||||
internals.patients[nurse].push_back(patient);
|
||||
}
|
||||
|
||||
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 pos = internals.patients.find(self);
|
||||
assert(pos != internals.patients.end());
|
||||
|
@ -377,14 +392,15 @@ inline void clear_patients(PyObject *self) {
|
|||
auto patients = std::move(pos->second);
|
||||
internals.patients.erase(pos);
|
||||
instance->has_patients = false;
|
||||
for (PyObject *&patient : patients)
|
||||
for (PyObject *&patient : patients) {
|
||||
Py_CLEAR(patient);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears all internal data from the instance and removes it from registered instances in
|
||||
/// preparation for deallocation.
|
||||
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:
|
||||
for (auto &v_h : values_and_holders(instance)) {
|
||||
|
@ -392,33 +408,40 @@ inline void clear_instance(PyObject *self) {
|
|||
|
||||
// We have to deregister before we call dealloc because, for virtual MI types, we still
|
||||
// need to be able to get the parent pointers.
|
||||
if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type))
|
||||
pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
|
||||
if (v_h.instance_registered()
|
||||
&& !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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Deallocate the value/holder layout internals:
|
||||
instance->deallocate_layout();
|
||||
|
||||
if (instance->weakrefs)
|
||||
if (instance->weakrefs) {
|
||||
PyObject_ClearWeakRefs(self);
|
||||
}
|
||||
|
||||
PyObject **dict_ptr = _PyObject_GetDictPtr(self);
|
||||
if (dict_ptr)
|
||||
if (dict_ptr) {
|
||||
Py_CLEAR(*dict_ptr);
|
||||
}
|
||||
|
||||
if (instance->has_patients)
|
||||
if (instance->has_patients) {
|
||||
clear_patients(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
|
||||
/// to destroy the C++ object itself, while the rest is Python bookkeeping.
|
||||
extern "C" inline void pybind11_object_dealloc(PyObject *self) {
|
||||
clear_instance(self);
|
||||
|
||||
auto type = Py_TYPE(self);
|
||||
auto *type = Py_TYPE(self);
|
||||
type->tp_free(self);
|
||||
|
||||
#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
|
||||
garbage collector (the GC will call type_traverse(), which will in
|
||||
turn find the newly constructed type in an invalid state) */
|
||||
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
||||
if (!heap_type)
|
||||
auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
||||
if (!heap_type) {
|
||||
pybind11_fail("make_object_base_type(): error allocating type!");
|
||||
}
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
auto type = &heap_type->ht_type;
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_name = name;
|
||||
type->tp_base = type_incref(&PyBaseObject_Type);
|
||||
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) */
|
||||
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());
|
||||
}
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
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__`.
|
||||
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
if (!dict)
|
||||
if (!dict) {
|
||||
dict = PyDict_New();
|
||||
}
|
||||
Py_XINCREF(dict);
|
||||
return dict;
|
||||
}
|
||||
|
@ -491,7 +517,8 @@ extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
|
|||
/// dynamic_attr: Support for `instance.__dict__ = dict()`.
|
||||
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
|
||||
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());
|
||||
return -1;
|
||||
}
|
||||
|
@ -518,7 +545,7 @@ extern "C" inline int pybind11_clear(PyObject *self) {
|
|||
|
||||
/// Give instances of this type a `__dict__` and opt into garbage collection.
|
||||
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_dictoffset = type->tp_basicsize; // place dict at the end
|
||||
type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
|
||||
|
@ -527,8 +554,7 @@ inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
|
|||
|
||||
static PyGetSetDef getset[] = {
|
||||
{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;
|
||||
}
|
||||
|
||||
|
@ -538,12 +564,14 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
|
|||
type_info *tinfo = nullptr;
|
||||
for (auto type : reinterpret_borrow<tuple>(Py_TYPE(obj)->tp_mro)) {
|
||||
tinfo = get_type_info((PyTypeObject *) type.ptr());
|
||||
if (tinfo && tinfo->get_buffer)
|
||||
if (tinfo && tinfo->get_buffer) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (view == nullptr || !tinfo || !tinfo->get_buffer) {
|
||||
if (view)
|
||||
if (view) {
|
||||
view->obj = nullptr;
|
||||
}
|
||||
PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
|
||||
return -1;
|
||||
}
|
||||
|
@ -561,15 +589,17 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
|
|||
view->buf = info->ptr;
|
||||
view->itemsize = info->itemsize;
|
||||
view->len = view->itemsize;
|
||||
for (auto s : info->shape)
|
||||
for (auto s : info->shape) {
|
||||
view->len *= s;
|
||||
}
|
||||
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());
|
||||
}
|
||||
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
|
||||
view->ndim = (int) info->ndim;
|
||||
view->strides = &info->strides[0];
|
||||
view->shape = &info->shape[0];
|
||||
view->strides = info->strides.data();
|
||||
view->shape = info->shape.data();
|
||||
}
|
||||
Py_INCREF(view->obj);
|
||||
return 0;
|
||||
|
@ -608,13 +638,14 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
|||
|
||||
object module_;
|
||||
if (rec.scope) {
|
||||
if (hasattr(rec.scope, "__module__"))
|
||||
if (hasattr(rec.scope, "__module__")) {
|
||||
module_ = rec.scope.attr("__module__");
|
||||
else if (hasattr(rec.scope, "__name__"))
|
||||
} else if (hasattr(rec.scope, "__name__")) {
|
||||
module_ = rec.scope.attr("__name__");
|
||||
}
|
||||
}
|
||||
|
||||
auto full_name = c_str(
|
||||
const auto *full_name = c_str(
|
||||
#if !defined(PYPY_VERSION)
|
||||
module_ ? str(module_).cast<std::string>() + "." + rec.name :
|
||||
#endif
|
||||
|
@ -624,39 +655,40 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
|||
if (rec.doc && options::show_user_defined_docstrings()) {
|
||||
/* Allocate memory for docstring (using PyObject_MALLOC, since
|
||||
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);
|
||||
memcpy((void *) tp_doc, rec.doc, size);
|
||||
std::memcpy((void *) tp_doc, rec.doc, size);
|
||||
}
|
||||
|
||||
auto &internals = get_internals();
|
||||
auto bases = tuple(rec.bases);
|
||||
auto base = (bases.empty()) ? internals.instance_base
|
||||
: bases[0].ptr();
|
||||
auto *base = (bases.empty()) ? internals.instance_base : bases[0].ptr();
|
||||
|
||||
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||
issue no Python C API calls which could potentially invoke the
|
||||
garbage collector (the GC will call type_traverse(), which will in
|
||||
turn find the newly constructed type in an invalid state) */
|
||||
auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr()
|
||||
: internals.default_metaclass;
|
||||
auto *metaclass
|
||||
= rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() : internals.default_metaclass;
|
||||
|
||||
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
||||
if (!heap_type)
|
||||
auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
||||
if (!heap_type) {
|
||||
pybind11_fail(std::string(rec.name) + ": Unable to create type object!");
|
||||
}
|
||||
|
||||
heap_type->ht_name = name.release().ptr();
|
||||
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = qualname.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
auto type = &heap_type->ht_type;
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_name = full_name;
|
||||
type->tp_doc = tp_doc;
|
||||
type->tp_base = type_incref((PyTypeObject *) base);
|
||||
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
|
||||
if (!bases.empty())
|
||||
if (!bases.empty()) {
|
||||
type->tp_bases = bases.release().ptr();
|
||||
}
|
||||
|
||||
/* Don't inherit base __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
|
||||
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
|
||||
#endif
|
||||
if (!rec.is_final)
|
||||
if (!rec.is_final) {
|
||||
type->tp_flags |= Py_TPFLAGS_BASETYPE;
|
||||
}
|
||||
|
||||
if (rec.dynamic_attr)
|
||||
if (rec.dynamic_attr) {
|
||||
enable_dynamic_attributes(heap_type);
|
||||
}
|
||||
|
||||
if (rec.buffer_protocol)
|
||||
if (rec.buffer_protocol) {
|
||||
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() + ")!");
|
||||
}
|
||||
|
||||
assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)
|
||||
: !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
|
||||
/* Register type with the parent scope */
|
||||
if (rec.scope)
|
||||
if (rec.scope) {
|
||||
setattr(rec.scope, rec.name, (PyObject *) type);
|
||||
else
|
||||
} else {
|
||||
Py_INCREF(type); // Keep it alive forever (reference leak)
|
||||
}
|
||||
|
||||
if (module_) // Needed by pydoc
|
||||
if (module_) { // Needed by pydoc
|
||||
setattr((PyObject *) type, "__module__", module_);
|
||||
}
|
||||
|
||||
PYBIND11_SET_OLDPY_QUALNAME(type, qualname);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,12 +26,14 @@ struct descr {
|
|||
char text[N + 1]{'\0'};
|
||||
|
||||
constexpr descr() = default;
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr descr(char const (&s)[N + 1]) : descr(s, make_index_sequence<N>()) {}
|
||||
|
||||
template <size_t... Is>
|
||||
constexpr descr(char const (&s)[N + 1], index_sequence<Is...>) : text{s[Is]..., '\0'} {}
|
||||
|
||||
template <typename... Chars>
|
||||
// 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() {
|
||||
|
@ -40,60 +42,116 @@ struct descr {
|
|||
};
|
||||
|
||||
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,
|
||||
index_sequence<Is1...>, index_sequence<Is2...>) {
|
||||
constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a,
|
||||
const descr<N2, Ts2...> &b,
|
||||
index_sequence<Is1...>,
|
||||
index_sequence<Is2...>) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b);
|
||||
return {a.text[Is1]..., b.text[Is2]...};
|
||||
}
|
||||
|
||||
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>());
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
constexpr descr<N - 1> _x(char const(&text)[N]) { return descr<N - 1>(text); }
|
||||
constexpr descr<0> _x(char const(&)[1]) { return {}; }
|
||||
constexpr descr<N - 1> const_name(char const (&text)[N]) {
|
||||
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...Digits> struct int_to_str<0, Digits...> {
|
||||
template <size_t Rem, size_t... 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)...);
|
||||
};
|
||||
|
||||
// Ternary description (like std::conditional)
|
||||
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]) {
|
||||
return _x(text1);
|
||||
constexpr enable_if_t<B, descr<N1 - 1>> const_name(char const (&text1)[N1], char const (&)[N2]) {
|
||||
return const_name(text1);
|
||||
}
|
||||
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]) {
|
||||
return _x(text2);
|
||||
constexpr enable_if_t<!B, descr<N2 - 1>> const_name(char const (&)[N1], char const (&text2)[N2]) {
|
||||
return const_name(text2);
|
||||
}
|
||||
|
||||
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>
|
||||
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;
|
||||
}
|
||||
|
||||
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 {}; }
|
||||
|
||||
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>
|
||||
constexpr auto concat(const descr<N, Ts...> &d, const Args &...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>
|
||||
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)
|
||||
|
|
|
@ -22,9 +22,10 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename> using cast_op_type = value_and_holder &;
|
||||
operator value_and_holder &() { return *value; }
|
||||
static constexpr auto name = _x<value_and_holder>();
|
||||
template <typename>
|
||||
using cast_op_type = value_and_holder &;
|
||||
explicit operator value_and_holder &() { return *value; }
|
||||
static constexpr auto name = const_name<value_and_holder>();
|
||||
|
||||
private:
|
||||
value_and_holder *value = nullptr;
|
||||
|
@ -33,15 +34,21 @@ private:
|
|||
PYBIND11_NAMESPACE_BEGIN(initimpl)
|
||||
|
||||
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(...)
|
||||
template <typename Class> using Cpp = typename Class::type;
|
||||
template <typename Class> using Alias = typename Class::type_alias;
|
||||
template <typename Class> using Holder = typename Class::holder_type;
|
||||
template <typename Class>
|
||||
using Cpp = typename Class::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.
|
||||
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)
|
||||
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
|
||||
// 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
|
||||
// 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).
|
||||
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)...); }
|
||||
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)...}; }
|
||||
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)...);
|
||||
}
|
||||
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
|
||||
// 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.
|
||||
template <typename Class>
|
||||
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));
|
||||
}
|
||||
template <typename Class>
|
||||
[[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 "
|
||||
"alias class: no `Alias<Class>(Class &&)` constructor available");
|
||||
}
|
||||
|
@ -94,8 +113,9 @@ void construct(...) {
|
|||
// construct an Alias from the returned base instance.
|
||||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
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
|
||||
// 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
|
||||
|
@ -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. 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>
|
||||
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);
|
||||
no_nullptr(ptr);
|
||||
// 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 "
|
||||
"is not an alias instance");
|
||||
}
|
||||
|
||||
v_h.value_ptr() = ptr;
|
||||
v_h.type->init_instance(v_h.inst, &holder);
|
||||
|
@ -148,20 +171,23 @@ 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.
|
||||
template <typename Class>
|
||||
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,
|
||||
"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));
|
||||
else
|
||||
} else {
|
||||
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
|
||||
// Alias instance (even if no the python-side inheritance is involved). The is intended for
|
||||
// cases where Alias initialization is always desired.
|
||||
template <typename Class>
|
||||
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");
|
||||
v_h.value_ptr() = new Alias<Class>(std::move(result));
|
||||
}
|
||||
|
@ -171,47 +197,75 @@ template <typename... Args>
|
|||
struct constructor {
|
||||
template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def("__init__", [](value_and_holder &v_h, Args... args) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
[](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
},
|
||||
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>
|
||||
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)...);
|
||||
else
|
||||
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||
}, is_new_style_constructor(), 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)...);
|
||||
} 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>
|
||||
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...);
|
||||
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<...>()
|
||||
template <typename... Args> struct alias_constructor {
|
||||
template <typename Class, typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int> = 0>
|
||||
template <typename... Args>
|
||||
struct alias_constructor {
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Alias<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...);
|
||||
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)
|
||||
template <typename CFunc, typename AFunc = void_type (*)(),
|
||||
typename = function_signature_t<CFunc>, typename = function_signature_t<AFunc>>
|
||||
template <typename CFunc,
|
||||
typename AFunc = void_type (*)(),
|
||||
typename = function_signature_t<CFunc>,
|
||||
typename = function_signature_t<AFunc>>
|
||||
struct factory;
|
||||
|
||||
// Specialization for py::init(Func)
|
||||
|
@ -219,6 +273,7 @@ template <typename Func, typename Return, typename... Args>
|
|||
struct factory<Func, void_type (*)(), Return(Args...)> {
|
||||
remove_reference_t<Func> class_factory;
|
||||
|
||||
// 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;
|
||||
|
@ -229,21 +284,31 @@ struct factory<Func, void_type (*)(), Return(Args...)> {
|
|||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra &...extra) && {
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def("__init__", [func = std::move(class_factory)]
|
||||
cl.def(
|
||||
"__init__",
|
||||
[func = std::move(class_factory)]
|
||||
#else
|
||||
auto &func = class_factory;
|
||||
cl.def("__init__", [func]
|
||||
cl.def(
|
||||
"__init__",
|
||||
[func]
|
||||
#endif
|
||||
(value_and_holder &v_h, Args... args) {
|
||||
construct<Class>(v_h, func(std::forward<Args>(args)...),
|
||||
Py_TYPE(v_h.inst) != v_h.type->type);
|
||||
}, 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)
|
||||
template <typename CFunc, typename AFunc,
|
||||
typename CReturn, typename... CArgs, typename AReturn, typename... AArgs>
|
||||
template <typename CFunc,
|
||||
typename AFunc,
|
||||
typename CReturn,
|
||||
typename... CArgs,
|
||||
typename AReturn,
|
||||
typename... AArgs>
|
||||
struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
|
||||
static_assert(sizeof...(CArgs) == sizeof...(AArgs),
|
||||
"pybind11::init(class_factory, alias_factory): class and alias factories "
|
||||
|
@ -262,23 +327,31 @@ struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
|
|||
// class (i.e. not inherited), the alias factory when `self` is a Python-side subtype.
|
||||
template <typename Class, typename... 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,
|
||||
"The two-argument version of `py::init()` can "
|
||||
"only be used if the class has an alias");
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
|
||||
cl.def(
|
||||
"__init__",
|
||||
[class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
|
||||
#else
|
||||
auto &class_func = class_factory;
|
||||
auto &alias_func = alias_factory;
|
||||
cl.def("__init__", [class_func, alias_func]
|
||||
cl.def(
|
||||
"__init__",
|
||||
[class_func, alias_func]
|
||||
#endif
|
||||
(value_and_holder &v_h, CArgs... args) {
|
||||
if (Py_TYPE(v_h.inst) == v_h.type->type)
|
||||
// If the instance type equals the registered type we don't have inheritance, so
|
||||
// don't need the alias and can construct using the class function:
|
||||
if (Py_TYPE(v_h.inst) == v_h.type->type) {
|
||||
// If the instance type equals the registered type we don't have inheritance,
|
||||
// so don't need the alias and can construct using the class function:
|
||||
construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
|
||||
else
|
||||
} else {
|
||||
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
}
|
||||
},
|
||||
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
|
||||
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>
|
||||
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);
|
||||
|
@ -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)
|
||||
template <typename Get, typename Set,
|
||||
typename = function_signature_t<Get>, typename = function_signature_t<Set>>
|
||||
template <typename Get,
|
||||
typename Set,
|
||||
typename = function_signature_t<Get>,
|
||||
typename = function_signature_t<Set>>
|
||||
struct pickle_factory;
|
||||
|
||||
template <typename Get, typename Set,
|
||||
typename RetState, typename Self, typename NewInstance, typename ArgState>
|
||||
template <typename Get,
|
||||
typename Set,
|
||||
typename RetState,
|
||||
typename Self,
|
||||
typename NewInstance,
|
||||
typename ArgState>
|
||||
struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
|
||||
static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value,
|
||||
"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<Set> set;
|
||||
|
||||
pickle_factory(Get get, Set set)
|
||||
: get(std::forward<Get>(get)), set(std::forward<Set>(set)) { }
|
||||
pickle_factory(Get get, Set set) : get(std::forward<Get>(get)), set(std::forward<Set>(set)) {}
|
||||
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra &...extra) && {
|
||||
cl.def("__getstate__", std::move(get));
|
||||
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def("__setstate__", [func = std::move(set)]
|
||||
cl.def(
|
||||
"__setstate__",
|
||||
[func = std::move(set)]
|
||||
#else
|
||||
auto &func = set;
|
||||
cl.def("__setstate__", [func]
|
||||
cl.def(
|
||||
"__setstate__",
|
||||
[func]
|
||||
#endif
|
||||
(value_and_holder &v_h, ArgState state) {
|
||||
setstate<Class>(v_h, func(std::forward<ArgState>(state)),
|
||||
Py_TYPE(v_h.inst) != v_h.type->type);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
setstate<Class>(
|
||||
v_h, func(std::forward<ArgState>(state)), Py_TYPE(v_h.inst) != v_h.type->type);
|
||||
},
|
||||
is_new_style_constructor(),
|
||||
extra...);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -11,8 +11,32 @@
|
|||
|
||||
#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)
|
||||
|
||||
using ExceptionTranslator = void (*)(std::exception_ptr);
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// Forward declarations
|
||||
inline PyTypeObject *make_static_property_type();
|
||||
inline PyTypeObject *make_default_metaclass();
|
||||
|
@ -21,28 +45,57 @@ 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
|
||||
// Thread Specific Storage (TSS) API.
|
||||
#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
|
||||
// `Py_LIMITED_API` anyway.
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
# define PYBIND11_TLS_KEY_REF Py_tss_t &
|
||||
# ifdef __GNUC__
|
||||
// Clang on macOS warns due to `Py_tss_NEEDS_INIT` not specifying an initializer
|
||||
// for every field.
|
||||
# define PYBIND11_TLS_KEY_INIT(var) \
|
||||
_Pragma("GCC diagnostic push") /**/ \
|
||||
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
|
||||
Py_tss_t var \
|
||||
= Py_tss_NEEDS_INIT; \
|
||||
_Pragma("GCC diagnostic pop")
|
||||
# else
|
||||
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
|
||||
# endif
|
||||
# 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
|
||||
# define PYBIND11_TLS_KEY_REF Py_tss_t *
|
||||
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr;
|
||||
# define PYBIND11_TLS_KEY_CREATE(var) \
|
||||
(((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
|
||||
#else
|
||||
// Usually an int but a long on Cygwin64 with Python 3.x
|
||||
# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0
|
||||
# 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
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) \
|
||||
PyThread_delete_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) \
|
||||
do { \
|
||||
PyThread_delete_key_value((key)); \
|
||||
PyThread_set_key_value((key), (value)); \
|
||||
} while (false)
|
||||
::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))
|
||||
# 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
|
||||
|
@ -66,8 +119,9 @@ struct type_hash {
|
|||
size_t operator()(const std::type_index &t) const {
|
||||
size_t hash = 5381;
|
||||
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;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
@ -94,30 +148,44 @@ struct override_hash {
|
|||
/// Whenever binary incompatible changes are made to this structure,
|
||||
/// `PYBIND11_INTERNALS_VERSION` must be incremented.
|
||||
struct internals {
|
||||
type_map<type_info *> registered_types_cpp; // std::type_index -> pybind11's type information
|
||||
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; // PyTypeObject* -> base type_info(s)
|
||||
// std::type_index -> pybind11's type information
|
||||
type_map<type_info *> registered_types_cpp;
|
||||
// PyTypeObject* -> base type_info(s)
|
||||
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;
|
||||
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash>
|
||||
inactive_override_cache;
|
||||
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
|
||||
std::unordered_map<const PyObject *, std::vector<PyObject *>> patients;
|
||||
std::forward_list<void (*) (std::exception_ptr)> registered_exception_translators;
|
||||
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions
|
||||
std::vector<PyObject *> loader_patient_stack; // Used by `loader_life_support`
|
||||
std::forward_list<std::string> static_strings; // Stores the std::strings backing detail::c_str()
|
||||
std::forward_list<ExceptionTranslator> registered_exception_translators;
|
||||
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across
|
||||
// extensions
|
||||
#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 *default_metaclass;
|
||||
PyObject *instance_base;
|
||||
#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;
|
||||
~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().
|
||||
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is called.
|
||||
// PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does 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
|
||||
// of those have anything to do with CPython internals.
|
||||
// PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator.
|
||||
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is
|
||||
// called. PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does
|
||||
// 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 of those have anything to do with CPython internals. PyMem_RawFree *requires*
|
||||
// that the `tstate` be allocated with the CPython allocator.
|
||||
PYBIND11_TLS_FREE(tstate);
|
||||
}
|
||||
#endif
|
||||
|
@ -139,7 +207,9 @@ struct type_info {
|
|||
void *get_buffer_data = nullptr;
|
||||
void *(*module_local_load)(PyObject *, const type_info *) = nullptr;
|
||||
/* 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;
|
||||
/* True if there is no multiple inheritance in this type's inheritance tree */
|
||||
bool simple_ancestors : 1;
|
||||
|
@ -149,9 +219,6 @@ struct type_info {
|
|||
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!
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
|
@ -210,11 +277,15 @@ struct type_info {
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \
|
||||
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
|
||||
#define PYBIND11_INTERNALS_ID \
|
||||
"__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" \
|
||||
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" 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
|
||||
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
|
||||
|
@ -223,21 +294,104 @@ inline internals **&get_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) {
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (p) std::rethrow_exception(p);
|
||||
} catch (error_already_set &e) { e.restore(); return;
|
||||
} catch (const builtin_exception &e) { e.set_error(); return;
|
||||
} catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return;
|
||||
} catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return;
|
||||
} catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
|
||||
} 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;
|
||||
std::rethrow_exception(p);
|
||||
} catch (error_already_set &e) {
|
||||
handle_nested_exception(e, p);
|
||||
e.restore();
|
||||
return;
|
||||
} catch (const builtin_exception &e) {
|
||||
// Could not use template since it's an abstract class.
|
||||
if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(e))) {
|
||||
handle_nested_exception(*nep, p);
|
||||
}
|
||||
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 (...) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
|
||||
raise_err(PyExc_RuntimeError, "Caught an unknown exception!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -245,18 +399,25 @@ inline void translate_exception(std::exception_ptr p) {
|
|||
#if !defined(__GLIBCXX__)
|
||||
inline void translate_local_exception(std::exception_ptr p) {
|
||||
try {
|
||||
if (p) std::rethrow_exception(p);
|
||||
} catch (error_already_set &e) { e.restore(); return;
|
||||
} catch (const builtin_exception &e) { e.set_error(); return;
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (error_already_set &e) {
|
||||
e.restore();
|
||||
return;
|
||||
} catch (const builtin_exception &e) {
|
||||
e.set_error();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// 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();
|
||||
if (internals_pp && *internals_pp)
|
||||
if (internals_pp && *internals_pp) {
|
||||
return **internals_pp;
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -282,7 +443,9 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
|||
(*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
|
||||
#endif
|
||||
} else {
|
||||
if (!internals_pp) internals_pp = new internals*();
|
||||
if (!internals_pp) {
|
||||
internals_pp = new internals *();
|
||||
}
|
||||
auto *&internals_ptr = *internals_pp;
|
||||
internals_ptr = new internals();
|
||||
#if defined(WITH_THREAD)
|
||||
|
@ -291,16 +454,16 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
|||
PyEval_InitThreads();
|
||||
# endif
|
||||
PyThreadState *tstate = PyThreadState_Get();
|
||||
#if PY_VERSION_HEX >= 0x03070000
|
||||
internals_ptr->tstate = PyThread_tss_alloc();
|
||||
if (!internals_ptr->tstate || (PyThread_tss_create(internals_ptr->tstate) != 0))
|
||||
pybind11_fail("get_internals: could not successfully initialize the TSS key!");
|
||||
PyThread_tss_set(internals_ptr->tstate, tstate);
|
||||
#else
|
||||
internals_ptr->tstate = PyThread_create_key();
|
||||
if (internals_ptr->tstate == -1)
|
||||
pybind11_fail("get_internals: could not successfully initialize the TLS key!");
|
||||
PyThread_set_key_value(internals_ptr->tstate, tstate);
|
||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
|
||||
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
|
||||
}
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
|
||||
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
|
||||
pybind11_fail("get_internals: could not successfully initialize the "
|
||||
"loader_life_support TSS key!");
|
||||
}
|
||||
# endif
|
||||
internals_ptr->istate = tstate->interp;
|
||||
#endif
|
||||
|
@ -313,9 +476,53 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
|||
return **internals_pp;
|
||||
}
|
||||
|
||||
/// Works like `internals.registered_types_cpp`, but for module-local registered types:
|
||||
inline type_map<type_info *> ®istered_local_types_cpp() {
|
||||
static type_map<type_info *> locals{};
|
||||
// the internals struct (above) is shared between all the modules. local_internals are only
|
||||
// for a single module. Any changes made to internals may require an update to
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -335,14 +542,14 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
/// 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
|
||||
/// 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 it = internals.shared_data.find(name);
|
||||
return it != internals.shared_data.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
return data;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -24,18 +24,21 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
|||
inline void erase_all(std::string &string, const std::string &search) {
|
||||
for (size_t pos = 0;;) {
|
||||
pos = string.find(search, pos);
|
||||
if (pos == std::string::npos) break;
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
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__)
|
||||
int status = 0;
|
||||
std::unique_ptr<char, void (*)(void *)> res{
|
||||
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free};
|
||||
if (status == 0)
|
||||
if (status == 0) {
|
||||
name = res.get();
|
||||
}
|
||||
#else
|
||||
detail::erase_all(name, "class ");
|
||||
detail::erase_all(name, "struct ");
|
||||
|
@ -46,7 +49,8 @@ PYBIND11_NOINLINE inline void clean_type_id(std::string &name) {
|
|||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/// 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());
|
||||
detail::clean_type_id(name);
|
||||
return name;
|
||||
|
|
|
@ -9,164 +9,191 @@
|
|||
|
||||
#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"
|
||||
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
|
||||
#elif defined(__GNUG__) || defined(__clang__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wconversion"
|
||||
# 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
|
||||
|
||||
// The C4127 suppression was introduced for Eigen 3.4.0. In theory we could
|
||||
// make it version specific, or even remove it later, but considering that
|
||||
// 1. C4127 is generally far more distracting than useful for modern template code, and
|
||||
// 2. we definitely want to ignore any MSVC warnings originating from Eigen code,
|
||||
// it is probably best to keep this around indefinitely.
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17
|
||||
# pragma warning(disable : 4127) // C4127: conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#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
|
||||
// move constructors that break things. We could detect this an explicitly copy, but an extra copy
|
||||
// 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)
|
||||
|
||||
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
|
||||
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
|
||||
template <typename MatrixType> using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType> using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType>
|
||||
using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType>
|
||||
using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
|
||||
using EigenIndex = Eigen::Index;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
|
||||
#else
|
||||
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
|
||||
#endif
|
||||
|
||||
// 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> 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>;
|
||||
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>
|
||||
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
|
||||
// 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
|
||||
// SelfAdjointView fall into this category.
|
||||
template <typename T> using is_eigen_other = 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>>>
|
||||
>;
|
||||
template <typename T>
|
||||
using is_eigen_other
|
||||
= 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()):
|
||||
template <bool EigenRowMajor> struct EigenConformable {
|
||||
template <bool EigenRowMajor>
|
||||
struct EigenConformable {
|
||||
bool conformable = false;
|
||||
EigenIndex rows = 0, cols = 0;
|
||||
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
|
||||
bool negativestrides = false; // If true, do not use stride!
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
EigenConformable(bool fits = false) : conformable{fits} {}
|
||||
// Matrix type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c,
|
||||
EigenIndex rstride, EigenIndex cstride) :
|
||||
conformable{true}, rows{r}, cols{c} {
|
||||
// TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
|
||||
if (rstride < 0 || cstride < 0) {
|
||||
negativestrides = true;
|
||||
} else {
|
||||
stride = {EigenRowMajor ? rstride : cstride /* outer stride */,
|
||||
EigenRowMajor ? cstride : rstride /* inner stride */ };
|
||||
}
|
||||
}
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride)
|
||||
: conformable{true}, rows{r}, cols{c},
|
||||
// TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
|
||||
// http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
|
||||
stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
|
||||
: (cstride > 0 ? cstride : 0) /* outer stride */,
|
||||
EigenRowMajor ? (cstride > 0 ? cstride : 0)
|
||||
: (rstride > 0 ? rstride : 0) /* inner stride */},
|
||||
negativestrides{rstride < 0 || cstride < 0} {}
|
||||
// Vector type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex 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,
|
||||
// matching strides, or a dimension size of 1 (in which case the stride value is irrelevant)
|
||||
return
|
||||
!negativestrides &&
|
||||
(props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() ||
|
||||
(EigenRowMajor ? cols : rows) == 1) &&
|
||||
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() ||
|
||||
(EigenRowMajor ? rows : cols) == 1);
|
||||
// matching strides, or a dimension size of 1 (in which case the stride value is
|
||||
// irrelevant)
|
||||
return !negativestrides
|
||||
&& (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
|
||||
|| (EigenRowMajor ? cols : rows) == 1)
|
||||
&& (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
|
||||
|| (EigenRowMajor ? rows : cols) == 1);
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
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>
|
||||
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>
|
||||
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
|
||||
template <typename Type_> struct EigenProps {
|
||||
template <typename Type_>
|
||||
struct EigenProps {
|
||||
using Type = Type_;
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StrideType = typename eigen_extract_stride<Type>::type;
|
||||
static constexpr EigenIndex
|
||||
rows = Type::RowsAtCompileTime,
|
||||
cols = Type::ColsAtCompileTime,
|
||||
static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
|
||||
size = Type::SizeAtCompileTime;
|
||||
static constexpr bool
|
||||
row_major = Type::IsRowMajor,
|
||||
vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
|
||||
fixed_rows = rows != Eigen::Dynamic,
|
||||
fixed_cols = cols != Eigen::Dynamic,
|
||||
static constexpr bool row_major = Type::IsRowMajor,
|
||||
vector
|
||||
= Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
|
||||
fixed_rows = rows != Eigen::Dynamic, 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>;
|
||||
static constexpr EigenIndex inner_stride = if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
|
||||
template <EigenIndex i, EigenIndex ifzero>
|
||||
using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
|
||||
static constexpr EigenIndex inner_stride
|
||||
= if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
|
||||
outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
|
||||
vector ? size : 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;
|
||||
vector ? size
|
||||
: 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
|
||||
// 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).
|
||||
static EigenConformable<row_major> conformable(const array &a) {
|
||||
const auto dims = a.ndim();
|
||||
if (dims < 1 || dims > 2)
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dims == 2) { // Matrix type: require exact match (or dynamic)
|
||||
|
||||
EigenIndex
|
||||
np_rows = a.shape(0),
|
||||
np_cols = a.shape(1),
|
||||
EigenIndex np_rows = a.shape(0), np_cols = a.shape(1),
|
||||
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
|
||||
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols))
|
||||
if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows)
|
||||
|| (PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
// is used, we want the (single) numpy stride value.
|
||||
// Otherwise we're storing an n-vector. Only one of the strides will be used, but
|
||||
// whichever is used, we want the (single) numpy stride value.
|
||||
const EigenIndex n = a.shape(0),
|
||||
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
|
||||
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 {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
|
||||
}
|
||||
if (fixed) {
|
||||
|
@ -176,48 +203,59 @@ template <typename Type_> struct EigenProps {
|
|||
if (fixed_cols) {
|
||||
// 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).
|
||||
if (cols != n) return false;
|
||||
if (cols != n) {
|
||||
return false;
|
||||
}
|
||||
return {1, n, stride};
|
||||
} // 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 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_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 =
|
||||
_x("numpy.ndarray[") + npy_format_descriptor<Scalar>::name +
|
||||
_x("[") + _x<fixed_rows>(_x<(size_t) rows>(), _x("m")) +
|
||||
_x(", ") + _x<fixed_cols>(_x<(size_t) cols>(), _x("n")) +
|
||||
_x("]") +
|
||||
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to be
|
||||
// satisfied: writeable=True (for a mutable reference), and, depending on the map's stride
|
||||
// options, possibly f_contiguous or c_contiguous. We include them in the descriptor output
|
||||
// to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to
|
||||
// see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you
|
||||
// *gave* a numpy.ndarray of the right type and dimensions.
|
||||
_x<show_writeable>(", flags.writeable", "") +
|
||||
_x<show_c_contiguous>(", flags.c_contiguous", "") +
|
||||
_x<show_f_contiguous>(", flags.f_contiguous", "") +
|
||||
_x("]");
|
||||
static constexpr auto descriptor
|
||||
= const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
|
||||
+ const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
|
||||
+ const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]")
|
||||
+
|
||||
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
|
||||
// be satisfied: writeable=True (for a mutable reference), and, depending on the map's
|
||||
// stride options, possibly f_contiguous or c_contiguous. We include them in the
|
||||
// descriptor output to provide some hint as to why a TypeError is occurring (otherwise
|
||||
// it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and
|
||||
// an error message that you *gave* a numpy.ndarray of the right type and dimensions.
|
||||
const_name<show_writeable>(", flags.writeable", "")
|
||||
+ const_name<show_c_contiguous>(", flags.c_contiguous", "")
|
||||
+ const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]");
|
||||
};
|
||||
|
||||
// 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.
|
||||
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);
|
||||
array a;
|
||||
if (props::vector)
|
||||
if (props::vector) {
|
||||
a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base);
|
||||
else
|
||||
a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() },
|
||||
src.data(), base);
|
||||
} else {
|
||||
a = array({src.rows(), src.cols()},
|
||||
{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_;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy
|
||||
// array that references the encapsulated data with a python-side reference to the capsule to tie
|
||||
// its destruction to that of any dependent python objects. Const-ness is determined by whether or
|
||||
// not the Type of the pointer given is const.
|
||||
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
|
||||
// numpy array that references the encapsulated data with a python-side reference to the capsule to
|
||||
// tie its destruction to that of any dependent python objects. Const-ness is determined by
|
||||
// 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>>
|
||||
handle eigen_encapsulate(Type *src) {
|
||||
capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
|
||||
|
@ -252,28 +290,35 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
|||
|
||||
bool load(handle src, bool convert) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Coerce into an array, but don't do type conversion yet; the copy below handles it.
|
||||
auto buf = array::ensure(src);
|
||||
|
||||
if (!buf)
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto dims = buf.ndim();
|
||||
if (dims < 1 || dims > 2)
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fits = props::conformable(buf);
|
||||
if (!fits)
|
||||
if (!fits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate the new type, then build a numpy reference into it
|
||||
value = Type(fits.rows, fits.cols);
|
||||
auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
|
||||
if (dims == 1) ref = ref.squeeze();
|
||||
else if (ref.ndim() == 1) buf = buf.squeeze();
|
||||
if (dims == 1) {
|
||||
ref = ref.squeeze();
|
||||
} else if (ref.ndim() == 1) {
|
||||
buf = buf.squeeze();
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
// Cast implementation
|
||||
template <typename CType>
|
||||
static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
|
||||
|
@ -309,7 +353,6 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
|
||||
// Normal returned non-reference, non-const value:
|
||||
static handle cast(Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
|
@ -320,14 +363,18 @@ public:
|
|||
}
|
||||
// lvalue reference return; default (automatic) becomes copy
|
||||
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;
|
||||
}
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
// const lvalue reference return; default (automatic) becomes copy
|
||||
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;
|
||||
}
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
// non-const pointer return
|
||||
|
@ -341,28 +388,32 @@ public:
|
|||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type *() { return &value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
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>;
|
||||
template <typename T>
|
||||
using cast_op_type = movable_cast_op_type<T>;
|
||||
|
||||
private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
// 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:
|
||||
using props = EigenProps<MapType>;
|
||||
|
||||
public:
|
||||
|
||||
// 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
|
||||
// have an appropriate keep_alive in place). We return a numpy array pointing directly at the
|
||||
// ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note
|
||||
// that this means you need to ensure you don't destroy the object in some other way (e.g. with
|
||||
// an appropriate keep_alive, or with a reference to a statically allocated matrix).
|
||||
// to stay around), but we'll allow it under the assumption that you know what you're doing
|
||||
// (and have an appropriate keep_alive in place). We return a numpy array pointing directly at
|
||||
// the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
|
||||
// Note that this means you need to ensure you don't destroy the object in some other way (e.g.
|
||||
// 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) {
|
||||
switch (policy) {
|
||||
case return_value_policy::copy:
|
||||
|
@ -386,43 +437,50 @@ public:
|
|||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = 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):
|
||||
template <typename Type> struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>>
|
||||
: eigen_map_caster<Type> {};
|
||||
template <typename 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
|
||||
// copying (it requires some extra effort in many cases).
|
||||
template <typename PlainObjectType, typename StrideType>
|
||||
struct type_caster<
|
||||
Eigen::Ref<PlainObjectType, 0, StrideType>,
|
||||
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>
|
||||
> : public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
|
||||
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
|
||||
: public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
|
||||
private:
|
||||
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
|
||||
using props = EigenProps<Type>;
|
||||
using Scalar = typename props::Scalar;
|
||||
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
|
||||
using Array = array_t<Scalar, 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)>;
|
||||
using Array
|
||||
= array_t<Scalar,
|
||||
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;
|
||||
// Delay construction (these have no default constructor)
|
||||
std::unique_ptr<MapType> map;
|
||||
std::unique_ptr<Type> ref;
|
||||
// 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
|
||||
// layout, or is an array of a type that needs to be converted). Using a numpy temporary
|
||||
// (rather than an Eigen temporary) saves an extra copy when we need both type conversion and
|
||||
// storage order conversion. (Note that we refuse to use this temporary copy when loading an
|
||||
// argument for a Ref<M> with M non-const, i.e. a read-write reference).
|
||||
// sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
|
||||
// incompatible layout, or is an array of a type that needs to be converted). Using a numpy
|
||||
// temporary (rather than an Eigen temporary) saves an extra copy when we need both type
|
||||
// conversion and storage order conversion. (Note that we refuse to use this temporary copy
|
||||
// when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
|
||||
Array copy_or_ref;
|
||||
|
||||
public:
|
||||
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
|
||||
// avoid a copy (because the copy is also going to do type conversion).
|
||||
// First check whether what we have is already an array of the right type. If not, we
|
||||
// can't avoid a copy (because the copy is also going to do type conversion).
|
||||
bool need_copy = !isinstance<Array>(src);
|
||||
|
||||
EigenConformable<props::row_major> fits;
|
||||
|
@ -433,13 +491,15 @@ public:
|
|||
|
||||
if (aref && (!need_writeable || aref.writeable())) {
|
||||
fits = props::conformable(aref);
|
||||
if (!fits) return false; // Incompatible dimensions
|
||||
if (!fits.template stride_compatible<props>())
|
||||
if (!fits) {
|
||||
return false; // Incompatible dimensions
|
||||
}
|
||||
if (!fits.template stride_compatible<props>()) {
|
||||
need_copy = true;
|
||||
else
|
||||
} else {
|
||||
copy_or_ref = std::move(aref);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
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
|
||||
// (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.
|
||||
if (!convert || need_writeable) return false;
|
||||
if (!convert || need_writeable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Array copy = Array::ensure(src);
|
||||
if (!copy) return false;
|
||||
fits = props::conformable(copy);
|
||||
if (!fits || !fits.template stride_compatible<props>())
|
||||
if (!copy) {
|
||||
return false;
|
||||
}
|
||||
fits = props::conformable(copy);
|
||||
if (!fits || !fits.template stride_compatible<props>()) {
|
||||
return false;
|
||||
}
|
||||
copy_or_ref = std::move(copy);
|
||||
loader_life_support::add_patient(copy_or_ref);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type *() { return ref.get(); }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type &() { return *ref; }
|
||||
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>;
|
||||
|
||||
private:
|
||||
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>
|
||||
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.
|
||||
// If both strides are fixed, use a default constructor:
|
||||
template <typename S> using stride_ctor_default = bool_constant<
|
||||
S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic &&
|
||||
std::is_default_constructible<S>::value>;
|
||||
template <typename S>
|
||||
using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_default_constructible<S>::value>;
|
||||
// Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
|
||||
// Eigen::Stride, and use it:
|
||||
template <typename S> using stride_ctor_dual = bool_constant<
|
||||
!stride_ctor_default<S>::value && std::is_constructible<S, EigenIndex, EigenIndex>::value>;
|
||||
template <typename S>
|
||||
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
|
||||
// it (passing whichever stride is dynamic).
|
||||
template <typename S> using stride_ctor_outer = bool_constant<
|
||||
!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value &&
|
||||
S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic &&
|
||||
std::is_constructible<S, EigenIndex>::value>;
|
||||
template <typename S> 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>
|
||||
using stride_ctor_outer
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::OuterStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
template <typename S>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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
|
||||
|
@ -515,14 +604,18 @@ private:
|
|||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
|
||||
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>;
|
||||
|
||||
public:
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
handle h = eigen_encapsulate<props>(new Matrix(src));
|
||||
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;
|
||||
|
||||
|
@ -531,7 +624,8 @@ public:
|
|||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator Type() = delete;
|
||||
template <typename> using cast_op_type = Type;
|
||||
template <typename>
|
||||
using cast_op_type = Type;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
|
@ -542,13 +636,13 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
|||
static constexpr bool rowMajor = Type::IsRowMajor;
|
||||
|
||||
bool load(handle src, bool) {
|
||||
if (!src)
|
||||
if (!src) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto obj = reinterpret_borrow<object>(src);
|
||||
object sparse_module = module_::import("scipy.sparse");
|
||||
object matrix_type = sparse_module.attr(
|
||||
rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
if (!type::handle_of(obj).is(matrix_type)) {
|
||||
try {
|
||||
|
@ -564,12 +658,18 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
|||
auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
|
||||
auto nnz = obj.attr("nnz").cast<Index>();
|
||||
|
||||
if (!values || !innerIndices || !outerIndices)
|
||||
if (!values || !innerIndices || !outerIndices) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = Eigen::MappedSparseMatrix<Scalar, Type::Flags, StorageIndex>(
|
||||
shape[0].cast<Index>(), shape[1].cast<Index>(), nnz,
|
||||
outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data());
|
||||
value = EigenMapSparseMatrix<Scalar,
|
||||
Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(shape[0].cast<Index>(),
|
||||
shape[1].cast<Index>(),
|
||||
nnz,
|
||||
outerIndices.mutable_data(),
|
||||
innerIndices.mutable_data(),
|
||||
values.mutable_data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -577,28 +677,23 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
|||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
const_cast<Type &>(src).makeCompressed();
|
||||
|
||||
object matrix_type = module_::import("scipy.sparse").attr(
|
||||
rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
object matrix_type
|
||||
= module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
array data(src.nonZeros(), src.valuePtr());
|
||||
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
|
||||
array innerIndices(src.nonZeros(), src.innerIndexPtr());
|
||||
|
||||
return matrix_type(
|
||||
std::make_tuple(data, innerIndices, outerIndices),
|
||||
std::make_pair(src.rows(), src.cols())
|
||||
).release();
|
||||
return matrix_type(std::make_tuple(data, innerIndices, outerIndices),
|
||||
std::make_pair(src.rows(), src.cols()))
|
||||
.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type, _x<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[")
|
||||
+ npy_format_descriptor<Scalar>::name + _x("]"));
|
||||
PYBIND11_TYPE_CASTER(Type,
|
||||
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(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(__GNUG__) || defined(__clang__)
|
||||
# pragma GCC diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#include "pybind11.h"
|
||||
#include "eval.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#if defined(PYPY_VERSION)
|
||||
# error Embedding the interpreter is not supported with PyPy
|
||||
#endif
|
||||
|
@ -19,15 +22,11 @@
|
|||
#if PY_MAJOR_VERSION >= 3
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" PyObject *pybind11_init_impl_##name(); \
|
||||
extern "C" PyObject *pybind11_init_impl_##name() { \
|
||||
return pybind11_init_wrapper_##name(); \
|
||||
}
|
||||
extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); }
|
||||
#else
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" void pybind11_init_impl_##name(); \
|
||||
extern "C" void pybind11_init_impl_##name() { \
|
||||
pybind11_init_wrapper_##name(); \
|
||||
}
|
||||
extern "C" void pybind11_init_impl_##name() { pybind11_init_wrapper_##name(); }
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
|
@ -46,24 +45,22 @@
|
|||
}
|
||||
\endrst */
|
||||
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||
static ::pybind11::module_::module_def \
|
||||
PYBIND11_CONCAT(pybind11_module_def_, name); \
|
||||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
PYBIND11_TOSTRING(name), nullptr, \
|
||||
&PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \
|
||||
(PYBIND11_TOSTRING(name), \
|
||||
PYBIND11_CONCAT(pybind11_init_impl_, name)); \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
|
||||
|
||||
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \
|
||||
PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \
|
||||
& variable) // NOLINT(bugprone-macro-parentheses)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
@ -76,38 +73,131 @@ struct embedded_module {
|
|||
using init_t = void (*)();
|
||||
#endif
|
||||
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");
|
||||
}
|
||||
|
||||
auto result = PyImport_AppendInittab(name, init);
|
||||
if (result == -1)
|
||||
if (result == -1) {
|
||||
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)
|
||||
|
||||
/** \rst
|
||||
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
|
||||
optional parameter can be used to skip the registration of signal handlers (see the
|
||||
`Python documentation`_ for details). Calling this function again after the interpreter
|
||||
has already been initialized is a fatal error.
|
||||
optional `init_signal_handlers` parameter can be used to skip the registration of
|
||||
signal handlers (see the `Python documentation`_ for details). Calling this function
|
||||
again after the interpreter has already been initialized is a fatal error.
|
||||
|
||||
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
|
||||
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
|
||||
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
|
||||
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
|
||||
\endrst */
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true) {
|
||||
if (Py_IsInitialized() != 0)
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true,
|
||||
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");
|
||||
}
|
||||
|
||||
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||
|
||||
// Make .py files in the working directory available by default
|
||||
module_::import("sys").attr("path").cast<list>().append(".");
|
||||
detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
|
||||
/** \rst
|
||||
|
@ -154,8 +244,13 @@ inline void finalize_interpreter() {
|
|||
// during destruction), so we get the pointer-pointer here and check it after Py_Finalize().
|
||||
detail::internals **internals_ptr_ptr = detail::get_internals_pp();
|
||||
// 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]);
|
||||
}
|
||||
// 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();
|
||||
|
||||
|
@ -169,6 +264,8 @@ inline void finalize_interpreter() {
|
|||
Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
|
||||
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
|
||||
|
||||
#include <pybind11/embed.h>
|
||||
|
@ -180,8 +277,11 @@ inline void finalize_interpreter() {
|
|||
\endrst */
|
||||
class scoped_interpreter {
|
||||
public:
|
||||
scoped_interpreter(bool init_signal_handlers = true) {
|
||||
initialize_interpreter(init_signal_handlers);
|
||||
explicit scoped_interpreter(bool init_signal_handlers = true,
|
||||
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;
|
||||
|
@ -190,9 +290,10 @@ public:
|
|||
scoped_interpreter &operator=(scoped_interpreter &&) = delete;
|
||||
|
||||
~scoped_interpreter() {
|
||||
if (is_valid)
|
||||
if (is_valid) {
|
||||
finalize_interpreter();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_valid = true;
|
||||
|
|
|
@ -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
|
||||
|
||||
Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and
|
||||
|
@ -11,19 +11,19 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
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
|
||||
// `__builtins__` key to globals if not yet present.
|
||||
// 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__"))
|
||||
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
|
||||
#else
|
||||
|
@ -46,8 +46,9 @@ enum eval_mode {
|
|||
|
||||
template <eval_mode mode = eval_expr>
|
||||
object eval(const str &expr, object global = globals(), object local = object()) {
|
||||
if (!local)
|
||||
if (!local) {
|
||||
local = 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;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_input; break;
|
||||
case eval_single_statement: start = Py_single_input; break;
|
||||
case eval_statements: start = Py_file_input; break;
|
||||
default: pybind11_fail("invalid evaluation mode");
|
||||
case eval_expr:
|
||||
start = Py_eval_input;
|
||||
break;
|
||||
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());
|
||||
if (!result)
|
||||
if (!result) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return reinterpret_steal<object>(result);
|
||||
}
|
||||
|
||||
template <eval_mode mode = eval_expr, size_t N>
|
||||
object eval(const char (&s)[N], object global = globals(), object local = object()) {
|
||||
/* Support raw string literals by removing common leading whitespace */
|
||||
auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s))
|
||||
: str(s);
|
||||
return eval<mode>(expr, global, local);
|
||||
auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) : str(s);
|
||||
return eval<mode>(expr, std::move(global), std::move(local));
|
||||
}
|
||||
|
||||
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>
|
||||
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
|
||||
|
@ -102,17 +110,25 @@ object eval_file(str) {
|
|||
#else
|
||||
template <eval_mode mode = eval_statements>
|
||||
object eval_file(str fname, object global = globals(), object local = object()) {
|
||||
if (!local)
|
||||
if (!local) {
|
||||
local = global;
|
||||
}
|
||||
|
||||
detail::ensure_builtins_in_globals(global);
|
||||
|
||||
int start = 0;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_input; break;
|
||||
case eval_single_statement: start = Py_single_input; break;
|
||||
case eval_statements: start = Py_file_input; break;
|
||||
default: pybind11_fail("invalid evaluation mode");
|
||||
case eval_expr:
|
||||
start = Py_eval_input;
|
||||
break;
|
||||
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;
|
||||
|
@ -123,9 +139,8 @@ object eval_file(str fname, object global = globals(), object local = object())
|
|||
FILE *f = _Py_fopen(fname.ptr(), "r");
|
||||
# else
|
||||
/* No unicode support in open() :( */
|
||||
auto fobj = reinterpret_steal<object>(PyFile_FromString(
|
||||
const_cast<char *>(fname_str.c_str()),
|
||||
const_cast<char*>("r")));
|
||||
auto fobj = reinterpret_steal<object>(
|
||||
PyFile_FromString(const_cast<char *>(fname_str.c_str()), const_cast<char *>("r")));
|
||||
FILE *f = nullptr;
|
||||
if (fobj)
|
||||
f = PyFile_AsFile(fobj.ptr());
|
||||
|
@ -136,17 +151,26 @@ object eval_file(str fname, object global = globals(), object local = object())
|
|||
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
|
||||
}
|
||||
|
||||
#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);
|
||||
// In Python2, this should be encoded by getfilesystemencoding.
|
||||
// We don't boher setting it since Python2 is past EOL anyway.
|
||||
// See PR#3233
|
||||
# if PY_VERSION_HEX >= 0x03000000
|
||||
if (!global.contains("__file__")) {
|
||||
global["__file__"] = std::move(fname);
|
||||
}
|
||||
# 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();
|
||||
}
|
||||
return reinterpret_steal<object>(result);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
@ -25,12 +26,15 @@ public:
|
|||
bool load(handle src, bool convert) {
|
||||
if (src.is_none()) {
|
||||
// Defer accepting None to other overloads (if we aren't in convert mode):
|
||||
if (!convert) return false;
|
||||
if (!convert) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isinstance<function>(src))
|
||||
if (!isinstance<function>(src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto func = reinterpret_borrow<function>(src);
|
||||
|
||||
|
@ -43,10 +47,10 @@ public:
|
|||
captured variables), in which case the roundtrip can be avoided.
|
||||
*/
|
||||
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)) {
|
||||
auto c = reinterpret_borrow<capsule>(cfunc_self);
|
||||
auto rec = (function_record *) c;
|
||||
auto *rec = (function_record *) c;
|
||||
|
||||
while (rec != nullptr) {
|
||||
if (rec->is_stateless
|
||||
|
@ -69,7 +73,13 @@ public:
|
|||
// ensure GIL is held during functor destruction
|
||||
struct func_handle {
|
||||
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 &operator=(const func_handle &f_) {
|
||||
gil_scoped_acquire acq;
|
||||
|
@ -85,7 +95,7 @@ public:
|
|||
// to emulate 'move initialization capture' in C++11
|
||||
struct func_wrapper {
|
||||
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 {
|
||||
gil_scoped_acquire acq;
|
||||
object retval(hfunc.f(std::forward<Args>(args)...));
|
||||
|
@ -100,17 +110,21 @@ public:
|
|||
|
||||
template <typename Func>
|
||||
static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) {
|
||||
if (!f_)
|
||||
if (!f_) {
|
||||
return none().inc_ref();
|
||||
}
|
||||
|
||||
auto result = f_.template target<function_type>();
|
||||
if (result)
|
||||
if (result) {
|
||||
return cpp_function(*result, policy).release();
|
||||
}
|
||||
return cpp_function(std::forward<Func>(f_), policy).release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(type, _x("Callable[[") + concat(make_caster<Args>::name...) + _x("], ")
|
||||
+ make_caster<retval_type>::name + _x("]"));
|
||||
PYBIND11_TYPE_CASTER(type,
|
||||
const_name("Callable[[") + concat(make_caster<Args>::name...)
|
||||
+ const_name("], ") + make_caster<retval_type>::name
|
||||
+ const_name("]"));
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// forward declarations
|
||||
|
@ -22,7 +21,6 @@ PyThreadState *get_thread_state_unchecked();
|
|||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
|
||||
|
||||
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
||||
|
@ -50,7 +48,7 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
class gil_scoped_acquire {
|
||||
public:
|
||||
PYBIND11_NOINLINE gil_scoped_acquire() {
|
||||
auto const &internals = detail::get_internals();
|
||||
auto &internals = detail::get_internals();
|
||||
tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
|
||||
|
||||
if (!tstate) {
|
||||
|
@ -65,8 +63,9 @@ public:
|
|||
if (!tstate) {
|
||||
tstate = PyThreadState_New(internals.istate);
|
||||
# if !defined(NDEBUG)
|
||||
if (!tstate)
|
||||
if (!tstate) {
|
||||
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||
}
|
||||
# endif
|
||||
tstate->gilstate_counter = 0;
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
|
||||
|
@ -81,26 +80,28 @@ public:
|
|||
inc_ref();
|
||||
}
|
||||
|
||||
void inc_ref() {
|
||||
++tstate->gilstate_counter;
|
||||
}
|
||||
void inc_ref() { ++tstate->gilstate_counter; }
|
||||
|
||||
PYBIND11_NOINLINE void dec_ref() {
|
||||
--tstate->gilstate_counter;
|
||||
# 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!");
|
||||
if (tstate->gilstate_counter < 0)
|
||||
}
|
||||
if (tstate->gilstate_counter < 0) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
|
||||
}
|
||||
# endif
|
||||
if (tstate->gilstate_counter == 0) {
|
||||
# if !defined(NDEBUG)
|
||||
if (!release)
|
||||
if (!release) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||
}
|
||||
# endif
|
||||
PyThreadState_Clear(tstate);
|
||||
if (active)
|
||||
if (active) {
|
||||
PyThreadState_DeleteCurrent();
|
||||
}
|
||||
PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate);
|
||||
release = false;
|
||||
}
|
||||
|
@ -111,15 +112,15 @@ public:
|
|||
/// could be shutting down when this is called, as thread deletion is not
|
||||
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||
/// protect subsequent code.
|
||||
PYBIND11_NOINLINE void disarm() {
|
||||
active = false;
|
||||
}
|
||||
PYBIND11_NOINLINE void disarm() { active = false; }
|
||||
|
||||
PYBIND11_NOINLINE ~gil_scoped_acquire() {
|
||||
dec_ref();
|
||||
if (release)
|
||||
if (release) {
|
||||
PyEval_SaveThread();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PyThreadState *tstate = nullptr;
|
||||
bool release = true;
|
||||
|
@ -132,9 +133,12 @@ public:
|
|||
// `get_internals()` must be called here unconditionally in order to initialize
|
||||
// `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
|
||||
// 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();
|
||||
if (disassoc) {
|
||||
// Python >= 3.7 can remove this, it's an int before 3.7
|
||||
// NOLINTNEXTLINE(readability-qualified-auto)
|
||||
auto key = internals.tstate;
|
||||
PYBIND11_TLS_DELETE_VALUE(key);
|
||||
}
|
||||
|
@ -145,21 +149,24 @@ public:
|
|||
/// could be shutting down when this is called, as thread deletion is not
|
||||
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||
/// protect subsequent code.
|
||||
PYBIND11_NOINLINE void disarm() {
|
||||
active = false;
|
||||
}
|
||||
PYBIND11_NOINLINE void disarm() { active = false; }
|
||||
|
||||
~gil_scoped_release() {
|
||||
if (!tstate)
|
||||
if (!tstate) {
|
||||
return;
|
||||
}
|
||||
// `PyEval_RestoreThread()` should not be called if runtime is finalizing
|
||||
if (active)
|
||||
if (active) {
|
||||
PyEval_RestoreThread(tstate);
|
||||
}
|
||||
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;
|
||||
PYBIND11_TLS_REPLACE_VALUE(key, tstate);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PyThreadState *tstate;
|
||||
bool disassoc;
|
||||
|
@ -168,6 +175,7 @@ private:
|
|||
#elif defined(PYPY_VERSION)
|
||||
class gil_scoped_acquire {
|
||||
PyGILState_STATE state;
|
||||
|
||||
public:
|
||||
gil_scoped_acquire() { state = PyGILState_Ensure(); }
|
||||
~gil_scoped_acquire() { PyGILState_Release(state); }
|
||||
|
@ -176,6 +184,7 @@ public:
|
|||
|
||||
class gil_scoped_release {
|
||||
PyThreadState *state;
|
||||
|
||||
public:
|
||||
gil_scoped_release() { state = PyEval_SaveThread(); }
|
||||
~gil_scoped_release() { PyEval_RestoreThread(state); }
|
||||
|
|
|
@ -58,36 +58,31 @@ private:
|
|||
size_t utf8_remainder() const {
|
||||
const auto rbase = std::reverse_iterator<char *>(pbase());
|
||||
const auto rpptr = std::reverse_iterator<char *>(pptr());
|
||||
auto is_ascii = [](char c) {
|
||||
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_3b = [](char c) {
|
||||
return static_cast<unsigned char>(c) <= 0xEF;
|
||||
};
|
||||
auto is_ascii = [](char c) { 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_3b = [](char c) { return static_cast<unsigned char>(c) <= 0xEF; };
|
||||
// If the last character is ASCII, there are no incomplete code points
|
||||
if (is_ascii(*rpptr))
|
||||
if (is_ascii(*rpptr)) {
|
||||
return 0;
|
||||
}
|
||||
// Otherwise, work back from the end of the buffer and find the first
|
||||
// UTF-8 leading byte
|
||||
const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase;
|
||||
const auto leading = std::find_if(rpptr, rpend, is_leading);
|
||||
if (leading == rbase)
|
||||
if (leading == rbase) {
|
||||
return 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
|
||||
else if (dist == 1)
|
||||
} else if (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;
|
||||
}
|
||||
// else if (dist >= 3), at least 4 bytes before encountering an UTF-8
|
||||
// leading byte, either no remainder or invalid UTF-8.
|
||||
// Invalid UTF-8 will cause an exception later when converting
|
||||
|
@ -110,20 +105,19 @@ private:
|
|||
}
|
||||
|
||||
// Copy the remainder at the end of the buffer to the beginning:
|
||||
if (remainder > 0)
|
||||
if (remainder > 0) {
|
||||
std::memmove(pbase(), pptr() - remainder, remainder);
|
||||
}
|
||||
setp(pbase(), epptr());
|
||||
pbump(static_cast<int>(remainder));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sync() override {
|
||||
return _sync();
|
||||
}
|
||||
int sync() override { return _sync(); }
|
||||
|
||||
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")),
|
||||
pyflush(pyostream.attr("flush")) {
|
||||
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
|
||||
|
@ -132,14 +126,11 @@ public:
|
|||
pythonbuf(pythonbuf &&) = default;
|
||||
|
||||
/// Sync before destroy
|
||||
~pythonbuf() override {
|
||||
_sync();
|
||||
}
|
||||
~pythonbuf() override { _sync(); }
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
|
||||
/** \rst
|
||||
This a move-only guard that redirects output.
|
||||
|
||||
|
@ -160,7 +151,8 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
.. 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!";
|
||||
}
|
||||
\endrst */
|
||||
|
@ -171,15 +163,14 @@ protected:
|
|||
detail::pythonbuf buffer;
|
||||
|
||||
public:
|
||||
scoped_ostream_redirect(std::ostream &costream = std::cout,
|
||||
const object &pyostream = module_::import("sys").attr("stdout"))
|
||||
explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
|
||||
const object &pyostream
|
||||
= module_::import("sys").attr("stdout"))
|
||||
: costream(costream), buffer(pyostream) {
|
||||
old = costream.rdbuf(&buffer);
|
||||
}
|
||||
|
||||
~scoped_ostream_redirect() {
|
||||
costream.rdbuf(old);
|
||||
}
|
||||
~scoped_ostream_redirect() { costream.rdbuf(old); }
|
||||
|
||||
scoped_ostream_redirect(const scoped_ostream_redirect &) = delete;
|
||||
scoped_ostream_redirect(scoped_ostream_redirect &&other) = default;
|
||||
|
@ -187,7 +178,6 @@ public:
|
|||
scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete;
|
||||
};
|
||||
|
||||
|
||||
/** \rst
|
||||
Like `scoped_ostream_redirect`, but redirects cerr by default. This class
|
||||
is provided primary to make ``py::call_guard`` easier to make.
|
||||
|
@ -201,12 +191,12 @@ public:
|
|||
\endrst */
|
||||
class scoped_estream_redirect : public scoped_ostream_redirect {
|
||||
public:
|
||||
scoped_estream_redirect(std::ostream &costream = std::cerr,
|
||||
const object &pyostream = module_::import("sys").attr("stderr"))
|
||||
explicit scoped_estream_redirect(std::ostream &costream = std::cerr,
|
||||
const object &pyostream
|
||||
= module_::import("sys").attr("stderr"))
|
||||
: scoped_ostream_redirect(costream, pyostream) {}
|
||||
};
|
||||
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// Class to redirect output as a context manager. C++ backend.
|
||||
|
@ -217,15 +207,17 @@ class OstreamRedirect {
|
|||
std::unique_ptr<scoped_estream_redirect> redirect_stderr;
|
||||
|
||||
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) {}
|
||||
|
||||
void enter() {
|
||||
if (do_stdout_)
|
||||
if (do_stdout_) {
|
||||
redirect_stdout.reset(new scoped_ostream_redirect());
|
||||
if (do_stderr_)
|
||||
}
|
||||
if (do_stderr_) {
|
||||
redirect_stderr.reset(new scoped_estream_redirect());
|
||||
}
|
||||
}
|
||||
|
||||
void exit() {
|
||||
redirect_stdout.reset();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -11,24 +11,55 @@
|
|||
|
||||
#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(detail)
|
||||
|
||||
/// Enumeration with all supported operator types
|
||||
enum op_id : int {
|
||||
op_add, op_sub, op_mul, op_div, op_mod, 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
|
||||
op_add,
|
||||
op_sub,
|
||||
op_mul,
|
||||
op_div,
|
||||
op_mod,
|
||||
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 {
|
||||
|
@ -47,23 +78,32 @@ struct undefined_t { };
|
|||
inline self_t __self() { return self; }
|
||||
|
||||
/// 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
|
||||
template <op_id id, op_type ot, typename L, typename R> struct op_ {
|
||||
template <typename Class, typename... Extra> void execute(Class &cl, const Extra&... extra) const {
|
||||
template <op_id id, op_type ot, typename L, typename R>
|
||||
struct op_ {
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra &...extra) const {
|
||||
using Base = typename Class::type;
|
||||
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 op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (id == op_truediv || id == op_itruediv)
|
||||
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
|
||||
&op::execute, is_operator(), extra...);
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv)
|
||||
|| PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
|
||||
cl.def(id == op_itruediv ? "__idiv__"
|
||||
: 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 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>;
|
||||
|
@ -71,19 +111,25 @@ template <op_id id, op_type ot, typename L, typename R> struct op_ {
|
|||
cl.def(op::name(), &op::execute_cast, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (id == op_truediv || id == op_itruediv)
|
||||
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
|
||||
&op::execute, is_operator(), extra...);
|
||||
cl.def(id == op_itruediv ? "__idiv__"
|
||||
: ot == op_l ? "__div__"
|
||||
: "__rdiv__",
|
||||
&op::execute,
|
||||
is_operator(),
|
||||
extra...);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#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> \
|
||||
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const *name() { return "__" #id "__"; } \
|
||||
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> { \
|
||||
template <typename B, typename L, typename R> \
|
||||
struct op_impl<op_##id, op_r, B, L, R> { \
|
||||
static char const *name() { return "__" #rid "__"; } \
|
||||
static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \
|
||||
static B execute_cast(const R &r, const L &l) { return B(expr); } \
|
||||
|
@ -91,25 +137,30 @@ template <typename B, typename L, typename R> struct op_impl<op_##id, op_r, B, L
|
|||
inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) { \
|
||||
return op_<op_##id, op_l, self_t, self_t>(); \
|
||||
} \
|
||||
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const 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>(); \
|
||||
} \
|
||||
template <typename T> op_<op_##id, op_r, T, self_t> op(const T &, const self_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) \
|
||||
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
template <typename B, typename L, typename R> \
|
||||
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const *name() { return "__" #id "__"; } \
|
||||
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 &) { \
|
||||
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) \
|
||||
template <typename B, typename L> struct op_impl<op_##id, op_u, B, L, undefined_t> { \
|
||||
template <typename B, typename L> \
|
||||
struct op_impl<op_##id, op_u, B, L, undefined_t> { \
|
||||
static char const *name() { return "__" #id "__"; } \
|
||||
static auto execute(const L &l) -> decltype(expr) { return expr; } \
|
||||
static B execute_cast(const L &l) { return B(expr); } \
|
||||
|
@ -167,7 +218,3 @@ using detail::self;
|
|||
using detail::hash;
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
|
|
@ -15,7 +15,6 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|||
|
||||
class options {
|
||||
public:
|
||||
|
||||
// Default RAII constructor, which leaves settings as they currently are.
|
||||
options() : previous_state(global_state()) {}
|
||||
|
||||
|
@ -24,23 +23,35 @@ public:
|
|||
options &operator=(const options &) = delete;
|
||||
|
||||
// Destructor, which restores settings that were in effect before.
|
||||
~options() {
|
||||
global_state() = previous_state;
|
||||
}
|
||||
~options() { global_state() = previous_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):
|
||||
|
||||
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; }
|
||||
|
||||
|
@ -48,10 +59,10 @@ public:
|
|||
void *operator new(size_t) = delete;
|
||||
|
||||
private:
|
||||
|
||||
struct state {
|
||||
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() {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -10,42 +10,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include "detail/common.h"
|
||||
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <valarray>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
// See `detail/common.h` for implementation of these guards.
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
# include <optional>
|
||||
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
|
||||
# include <experimental/optional>
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
// 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>)
|
||||
#if defined(PYBIND11_HAS_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
|
||||
|
||||
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
|
||||
/// forwarding a container element). Typically used indirect via forwarded_type(), below.
|
||||
template <typename T, typename U>
|
||||
using forwarded_type = conditional_t<
|
||||
std::is_lvalue_reference<T>::value, remove_reference_t<U> &, remove_reference_t<U> &&>;
|
||||
using forwarded_type = conditional_t<std::is_lvalue_reference<T>::value,
|
||||
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
|
||||
/// 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));
|
||||
}
|
||||
|
||||
template <typename Type, typename Key> struct set_caster {
|
||||
template <typename Type, typename Key>
|
||||
struct set_caster {
|
||||
using type = Type;
|
||||
using key_conv = make_caster<Key>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<pybind11::set>(src))
|
||||
if (!isinstance<pybind11::set>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto s = reinterpret_borrow<pybind11::set>(src);
|
||||
value.clear();
|
||||
for (auto entry : s) {
|
||||
key_conv conv;
|
||||
if (!conv.load(entry, convert))
|
||||
if (!conv.load(entry, convert)) {
|
||||
return false;
|
||||
}
|
||||
value.insert(cast_op<Key &&>(std::move(conv)));
|
||||
}
|
||||
return true;
|
||||
|
@ -84,35 +72,40 @@ template <typename Type, typename Key> struct set_caster {
|
|||
|
||||
template <typename T>
|
||||
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);
|
||||
}
|
||||
pybind11::set s;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(key_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_ || !s.add(value_))
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
key_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_ || !s.add(value_)) {
|
||||
return handle();
|
||||
}
|
||||
}
|
||||
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>
|
||||
struct map_caster {
|
||||
using key_conv = make_caster<Key>;
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<dict>(src))
|
||||
if (!isinstance<dict>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto d = reinterpret_borrow<dict>(src);
|
||||
value.clear();
|
||||
for (auto it : d) {
|
||||
key_conv kconv;
|
||||
value_conv vconv;
|
||||
if (!kconv.load(it.first.ptr(), convert) ||
|
||||
!vconv.load(it.second.ptr(), convert))
|
||||
if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) {
|
||||
return false;
|
||||
}
|
||||
value.emplace(cast_op<Key &&>(std::move(kconv)), cast_op<Value &&>(std::move(vconv)));
|
||||
}
|
||||
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);
|
||||
}
|
||||
for (auto &&kv : src) {
|
||||
auto key = reinterpret_steal<object>(key_conv::cast(forward_like<T>(kv.first), policy_key, parent));
|
||||
auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy_value, parent));
|
||||
if (!key || !value)
|
||||
auto key = reinterpret_steal<object>(
|
||||
key_conv::cast(forward_like<T>(kv.first), policy_key, parent));
|
||||
auto value = reinterpret_steal<object>(
|
||||
value_conv::cast(forward_like<T>(kv.second), policy_value, parent));
|
||||
if (!key || !value) {
|
||||
return handle();
|
||||
}
|
||||
d[key] = value;
|
||||
}
|
||||
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>;
|
||||
|
||||
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;
|
||||
}
|
||||
auto s = reinterpret_borrow<sequence>(src);
|
||||
value.clear();
|
||||
reserve_maybe(s, &value);
|
||||
for (auto it : s) {
|
||||
value_conv conv;
|
||||
if (!conv.load(it, convert))
|
||||
if (!conv.load(it, convert)) {
|
||||
return false;
|
||||
}
|
||||
value.push_back(cast_op<Value &&>(std::move(conv)));
|
||||
}
|
||||
return true;
|
||||
|
@ -170,39 +171,44 @@ private:
|
|||
public:
|
||||
template <typename T>
|
||||
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);
|
||||
}
|
||||
list l(src.size());
|
||||
size_t index = 0;
|
||||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_)
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_) {
|
||||
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();
|
||||
}
|
||||
|
||||
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>>
|
||||
: list_caster<std::vector<Type, Alloc>, Type> { };
|
||||
template <typename Type, typename Alloc>
|
||||
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>>
|
||||
: list_caster<std::deque<Type, Alloc>, Type> { };
|
||||
template <typename Type, typename Alloc>
|
||||
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>>
|
||||
: list_caster<std::list<Type, Alloc>, Type> { };
|
||||
template <typename Type, typename Alloc>
|
||||
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>;
|
||||
|
||||
private:
|
||||
template <bool R = Resizable>
|
||||
bool require_size(enable_if_t<R, size_t> size) {
|
||||
if (value.size() != size)
|
||||
if (value.size() != size) {
|
||||
value.resize(size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
template <bool R = Resizable>
|
||||
|
@ -212,16 +218,19 @@ private:
|
|||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<sequence>(src))
|
||||
if (!isinstance<sequence>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto l = reinterpret_borrow<sequence>(src);
|
||||
if (!require_size(l.size()))
|
||||
if (!require_size(l.size())) {
|
||||
return false;
|
||||
}
|
||||
size_t ctr = 0;
|
||||
for (auto it : l) {
|
||||
value_conv conv;
|
||||
if (!conv.load(it, convert))
|
||||
if (!conv.load(it, convert)) {
|
||||
return false;
|
||||
}
|
||||
value[ctr++] = cast_op<Value &&>(std::move(conv));
|
||||
}
|
||||
return true;
|
||||
|
@ -230,49 +239,63 @@ public:
|
|||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
list l(src.size());
|
||||
size_t index = 0;
|
||||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_)
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_) {
|
||||
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();
|
||||
}
|
||||
|
||||
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>
|
||||
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>>
|
||||
: array_caster<std::valarray<Type>, Type, true> { };
|
||||
template <typename Type>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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
|
||||
template<typename T> struct optional_caster {
|
||||
using value_conv = make_caster<typename T::value_type>;
|
||||
template <typename Type, typename Value = typename Type::value_type>
|
||||
struct optional_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
template <typename T_>
|
||||
static handle cast(T_ &&src, return_value_policy policy, handle parent) {
|
||||
if (!src)
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
if (!src) {
|
||||
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) {
|
||||
|
@ -283,29 +306,32 @@ template<typename T> struct optional_caster {
|
|||
return true; // default-constructed value is already empty
|
||||
}
|
||||
value_conv inner_caster;
|
||||
if (!inner_caster.load(src, convert))
|
||||
if (!inner_caster.load(src, convert)) {
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
template<typename T> struct type_caster<std::optional<T>>
|
||||
: public optional_caster<std::optional<T>> {};
|
||||
template <typename T>
|
||||
struct type_caster<std::optional<T>> : public optional_caster<std::optional<T>> {};
|
||||
|
||||
template<> struct type_caster<std::nullopt_t>
|
||||
: public void_caster<std::nullopt_t> {};
|
||||
template <>
|
||||
struct type_caster<std::nullopt_t> : public void_caster<std::nullopt_t> {};
|
||||
#endif
|
||||
|
||||
#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>> {};
|
||||
|
||||
template<> struct type_caster<std::experimental::nullopt_t>
|
||||
template <>
|
||||
struct type_caster<std::experimental::nullopt_t>
|
||||
: public void_caster<std::experimental::nullopt_t> {};
|
||||
#endif
|
||||
|
||||
|
@ -335,7 +361,8 @@ struct visit_helper {
|
|||
};
|
||||
|
||||
/// Generic variant caster
|
||||
template <typename Variant> struct variant_caster;
|
||||
template <typename Variant>
|
||||
struct variant_caster;
|
||||
|
||||
template <template <typename...> class V, typename... Ts>
|
||||
struct variant_caster<V<Ts...>> {
|
||||
|
@ -358,8 +385,9 @@ struct variant_caster<V<Ts...>> {
|
|||
// 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
|
||||
// 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 load_alternative(src, convert, type_list<Ts...>{});
|
||||
}
|
||||
|
||||
|
@ -370,7 +398,9 @@ struct variant_caster<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)
|
||||
|
@ -390,7 +420,3 @@ inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
|
|||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../cast.h"
|
||||
#include "../pybind11.h"
|
||||
#include "../pytypes.h"
|
||||
|
||||
#include "../detail/common.h"
|
||||
#include "../detail/descr.h"
|
||||
#include "../cast.h"
|
||||
#include "../pytypes.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -30,7 +29,8 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if defined(PYBIND11_HAS_FILESYSTEM)
|
||||
template<typename T> struct path_caster {
|
||||
template <typename T>
|
||||
struct path_caster {
|
||||
|
||||
private:
|
||||
static PyObject *unicode_from_fs_native(const std::string &w) {
|
||||
|
@ -38,8 +38,7 @@ private:
|
|||
return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size()));
|
||||
# else
|
||||
// PyPy mistakenly declares the first parameter as non-const.
|
||||
return PyUnicode_DecodeFSDefaultAndSize(
|
||||
const_cast<char*>(w.c_str()), ssize_t(w.size()));
|
||||
return PyUnicode_DecodeFSDefaultAndSize(const_cast<char *>(w.c_str()), ssize_t(w.size()));
|
||||
# endif
|
||||
}
|
||||
|
||||
|
@ -50,7 +49,8 @@ private:
|
|||
public:
|
||||
static handle cast(const T &path, return_value_policy, handle) {
|
||||
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")
|
||||
.attr("Path")(reinterpret_steal<object>(py_str))
|
||||
.release();
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -68,7 +68,7 @@ public:
|
|||
PyObject *native = nullptr;
|
||||
if constexpr (std::is_same_v<typename T::value_type, char>) {
|
||||
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
|
||||
// must not be free'd.
|
||||
value = c_str;
|
||||
|
@ -76,7 +76,7 @@ public:
|
|||
}
|
||||
} else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) {
|
||||
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.
|
||||
value = c_str; // Copies the string.
|
||||
PyMem_Free(c_str);
|
||||
|
@ -92,11 +92,11 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(T, _x("os.PathLike"));
|
||||
PYBIND11_TYPE_CASTER(T, const_name("os.PathLike"));
|
||||
};
|
||||
|
||||
template<> struct type_caster<std::filesystem::path>
|
||||
: public path_caster<std::filesystem::path> {};
|
||||
template <>
|
||||
struct type_caster<std::filesystem::path> : public path_caster<std::filesystem::path> {};
|
||||
#endif // PYBIND11_HAS_FILESYSTEM
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
|
|
@ -19,17 +19,28 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/* SFINAE helper class used by 'is_comparable */
|
||||
template <typename T> struct container_traits {
|
||||
template <typename T2> static std::true_type test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>())*);
|
||||
template <typename T2> 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(...);
|
||||
template <typename T>
|
||||
struct container_traits {
|
||||
template <typename T2>
|
||||
static std::true_type
|
||||
test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>()) *);
|
||||
template <typename T2>
|
||||
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_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_comparable
|
||||
= std::is_same<std::true_type, decltype(test_comparable<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;
|
||||
};
|
||||
|
||||
|
@ -40,30 +51,33 @@ struct is_comparable : std::false_type { };
|
|||
/* For non-map data structures, check whether operator== can be instantiated */
|
||||
template <typename T>
|
||||
struct is_comparable<
|
||||
T, enable_if_t<container_traits<T>::is_element &&
|
||||
container_traits<T>::is_comparable>>
|
||||
T,
|
||||
enable_if_t<container_traits<T>::is_element && container_traits<T>::is_comparable>>
|
||||
: 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>
|
||||
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> {
|
||||
static constexpr const bool value =
|
||||
is_comparable<typename T::value_type>::value;
|
||||
static constexpr const bool value = is_comparable<typename T::value_type>::value;
|
||||
};
|
||||
|
||||
/* For pairs, recursively check the two data types */
|
||||
template <typename T>
|
||||
struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> {
|
||||
static constexpr const bool value =
|
||||
is_comparable<typename T::first_type>::value &&
|
||||
is_comparable<typename T::second_type>::value;
|
||||
static constexpr const bool value = is_comparable<typename T::first_type>::value
|
||||
&& is_comparable<typename T::second_type>::value;
|
||||
};
|
||||
|
||||
/* Fallback functions */
|
||||
template <typename, typename, typename... Args> void vector_if_copy_constructible(const Args &...) { }
|
||||
template <typename, typename, typename... 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, typename, typename... Args>
|
||||
void vector_if_copy_constructible(const Args &...) {}
|
||||
template <typename, typename, typename... 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_>
|
||||
void vector_if_copy_constructible(enable_if_t<is_copy_constructible<Vector>::value, Class_> &cl) {
|
||||
|
@ -77,53 +91,55 @@ void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_>
|
|||
cl.def(self == self);
|
||||
cl.def(self != self);
|
||||
|
||||
cl.def("count",
|
||||
[](const Vector &v, const T &x) {
|
||||
return std::count(v.begin(), v.end(), x);
|
||||
},
|
||||
cl.def(
|
||||
"count",
|
||||
[](const Vector &v, const T &x) { return std::count(v.begin(), v.end(), 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);
|
||||
if (p != v.end())
|
||||
if (p != v.end()) {
|
||||
v.erase(p);
|
||||
else
|
||||
} else {
|
||||
throw value_error();
|
||||
}
|
||||
},
|
||||
arg("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__",
|
||||
[](const Vector &v, const T &x) {
|
||||
return std::find(v.begin(), v.end(), x) != v.end();
|
||||
},
|
||||
cl.def(
|
||||
"__contains__",
|
||||
[](const Vector &v, const T &x) { return std::find(v.begin(), v.end(), x) != v.end(); },
|
||||
arg("x"),
|
||||
"Return true the container contains ``x``"
|
||||
);
|
||||
"Return true the container contains ``x``");
|
||||
}
|
||||
|
||||
// Vector modifiers -- requires a copyable vector_type:
|
||||
// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems
|
||||
// silly to allow deletion but not insertion, so include them here too.)
|
||||
// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it
|
||||
// seems silly to allow deletion but not insertion, so include them here too.)
|
||||
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 SizeType = typename Vector::size_type;
|
||||
using DiffType = typename Vector::difference_type;
|
||||
|
||||
auto wrap_i = [](DiffType i, SizeType n) {
|
||||
if (i < 0)
|
||||
if (i < 0) {
|
||||
i += n;
|
||||
if (i < 0 || (SizeType)i >= n)
|
||||
}
|
||||
if (i < 0 || (SizeType) i >= n) {
|
||||
throw index_error();
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
cl.def("append",
|
||||
cl.def(
|
||||
"append",
|
||||
[](Vector &v, const T &value) { v.push_back(value); },
|
||||
arg("x"),
|
||||
"Add an item to the end of the list");
|
||||
|
@ -131,25 +147,20 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
|||
cl.def(init([](const iterable &it) {
|
||||
auto v = std::unique_ptr<Vector>(new Vector());
|
||||
v->reserve(len_hint(it));
|
||||
for (handle h : it)
|
||||
for (handle h : it) {
|
||||
v->push_back(h.cast<T>());
|
||||
}
|
||||
return v.release();
|
||||
}));
|
||||
|
||||
cl.def("clear",
|
||||
[](Vector &v) {
|
||||
v.clear();
|
||||
},
|
||||
"Clear the contents"
|
||||
);
|
||||
cl.def(
|
||||
"clear", [](Vector &v) { v.clear(); }, "Clear the contents");
|
||||
|
||||
cl.def("extend",
|
||||
[](Vector &v, const Vector &src) {
|
||||
v.insert(v.end(), src.begin(), src.end());
|
||||
},
|
||||
cl.def(
|
||||
"extend",
|
||||
[](Vector &v, const Vector &src) { v.insert(v.end(), src.begin(), src.end()); },
|
||||
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(
|
||||
"extend",
|
||||
|
@ -174,31 +185,36 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
|||
arg("L"),
|
||||
"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) {
|
||||
// Can't use wrap_i; i == v.size() is OK
|
||||
if (i < 0)
|
||||
if (i < 0) {
|
||||
i += v.size();
|
||||
if (i < 0 || (SizeType)i > v.size())
|
||||
}
|
||||
if (i < 0 || (SizeType) i > v.size()) {
|
||||
throw index_error();
|
||||
}
|
||||
v.insert(v.begin() + i, x);
|
||||
},
|
||||
arg("i") , arg("x"),
|
||||
"Insert an item at a given position."
|
||||
);
|
||||
arg("i"),
|
||||
arg("x"),
|
||||
"Insert an item at a given position.");
|
||||
|
||||
cl.def("pop",
|
||||
cl.def(
|
||||
"pop",
|
||||
[](Vector &v) {
|
||||
if (v.empty())
|
||||
if (v.empty()) {
|
||||
throw index_error();
|
||||
}
|
||||
T t = std::move(v.back());
|
||||
v.pop_back();
|
||||
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) {
|
||||
i = wrap_i(i, v.size());
|
||||
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;
|
||||
},
|
||||
arg("i"),
|
||||
"Remove and return the item at index ``i``"
|
||||
);
|
||||
"Remove and return the item at index ``i``");
|
||||
|
||||
cl.def("__setitem__",
|
||||
[wrap_i](Vector &v, DiffType i, const T &t) {
|
||||
cl.def("__setitem__", [wrap_i](Vector &v, DiffType i, const T &t) {
|
||||
i = wrap_i(i, v.size());
|
||||
v[(SizeType) i] = t;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
/// Slicing protocol
|
||||
cl.def(
|
||||
|
@ -222,8 +235,9 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
|||
[](const Vector &v, slice slice) -> Vector * {
|
||||
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();
|
||||
}
|
||||
|
||||
auto *seq = new Vector();
|
||||
seq->reserve((size_t) slicelength);
|
||||
|
@ -241,11 +255,14 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
|||
"__setitem__",
|
||||
[](Vector &v, slice slice, const Vector &value) {
|
||||
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();
|
||||
}
|
||||
|
||||
if (slicelength != value.size())
|
||||
throw std::runtime_error("Left and right hand size of slice assignment have different sizes!");
|
||||
if (slicelength != value.size()) {
|
||||
throw std::runtime_error(
|
||||
"Left and right hand size of slice assignment have different sizes!");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < slicelength; ++i) {
|
||||
v[start] = value[i];
|
||||
|
@ -254,21 +271,22 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
|||
},
|
||||
"Assign list elements using a slice object");
|
||||
|
||||
cl.def("__delitem__",
|
||||
cl.def(
|
||||
"__delitem__",
|
||||
[wrap_i](Vector &v, DiffType i) {
|
||||
i = wrap_i(i, v.size());
|
||||
v.erase(v.begin() + i);
|
||||
},
|
||||
"Delete the list elements at index ``i``"
|
||||
);
|
||||
"Delete the list elements at index ``i``");
|
||||
|
||||
cl.def(
|
||||
"__delitem__",
|
||||
[](Vector &v, slice slice) {
|
||||
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();
|
||||
}
|
||||
|
||||
if (step == 1 && false) {
|
||||
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>),
|
||||
// we have to access by copying; otherwise we return by reference.
|
||||
template <typename Vector> using vector_needs_copy = negation<
|
||||
std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>>;
|
||||
template <typename Vector>
|
||||
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
|
||||
template <typename Vector, typename Class_>
|
||||
|
@ -296,14 +316,17 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl)
|
|||
using ItType = typename Vector::iterator;
|
||||
|
||||
auto wrap_i = [](DiffType i, SizeType n) {
|
||||
if (i < 0)
|
||||
if (i < 0) {
|
||||
i += n;
|
||||
if (i < 0 || (SizeType)i >= n)
|
||||
}
|
||||
if (i < 0 || (SizeType) i >= n) {
|
||||
throw index_error();
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
cl.def("__getitem__",
|
||||
cl.def(
|
||||
"__getitem__",
|
||||
[wrap_i](Vector &v, DiffType i) -> T & {
|
||||
i = wrap_i(i, v.size());
|
||||
return v[(SizeType) i];
|
||||
|
@ -311,10 +334,10 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl)
|
|||
return_value_policy::reference_internal // ref + keepalive
|
||||
);
|
||||
|
||||
cl.def("__iter__",
|
||||
cl.def(
|
||||
"__iter__",
|
||||
[](Vector &v) {
|
||||
return make_iterator<
|
||||
return_value_policy::reference_internal, ItType, ItType, T&>(
|
||||
return make_iterator<return_value_policy::reference_internal, ItType, ItType, T &>(
|
||||
v.begin(), v.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
|
@ -328,52 +351,59 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl)
|
|||
using SizeType = typename Vector::size_type;
|
||||
using DiffType = typename Vector::difference_type;
|
||||
using ItType = typename Vector::iterator;
|
||||
cl.def("__getitem__",
|
||||
[](const Vector &v, DiffType i) -> T {
|
||||
if (i < 0 && (i += v.size()) < 0)
|
||||
cl.def("__getitem__", [](const Vector &v, DiffType i) -> T {
|
||||
if (i < 0 && (i += v.size()) < 0) {
|
||||
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(
|
||||
"__iter__",
|
||||
[](Vector &v) {
|
||||
return make_iterator<
|
||||
return_value_policy::copy, ItType, ItType, T>(
|
||||
v.begin(), v.end());
|
||||
return make_iterator<return_value_policy::copy, ItType, ItType, T>(v.begin(), v.end());
|
||||
},
|
||||
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)
|
||||
-> decltype(std::declval<std::ostream&>() << std::declval<typename Vector::value_type>(), void()) {
|
||||
template <typename Vector, typename Class_>
|
||||
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;
|
||||
|
||||
cl.def("__repr__",
|
||||
cl.def(
|
||||
"__repr__",
|
||||
[name](Vector &v) {
|
||||
std::ostringstream s;
|
||||
s << name << '[';
|
||||
for (size_type i = 0; i < v.size(); ++i) {
|
||||
s << v[i];
|
||||
if (i != v.size() - 1)
|
||||
if (i != v.size() - 1) {
|
||||
s << ", ";
|
||||
}
|
||||
}
|
||||
s << ']';
|
||||
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
|
||||
// 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>
|
||||
struct vector_has_data_and_format : std::false_type {};
|
||||
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 as the Intel compiler does not compile the enable_if_t part below
|
||||
|
@ -391,21 +421,32 @@ template <typename Vector, typename Class_, typename... Args>
|
|||
void vector_buffer_impl(Class_ &cl, std::true_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();
|
||||
|
||||
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) {
|
||||
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");
|
||||
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);
|
||||
ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
|
||||
|
@ -415,10 +456,10 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
|
|||
}
|
||||
Vector vec;
|
||||
vec.reserve((size_t) info.shape[0]);
|
||||
for (; p != end; p += step)
|
||||
for (; p != end; p += step) {
|
||||
vec.push_back(*p);
|
||||
}
|
||||
return vec;
|
||||
|
||||
}));
|
||||
|
||||
return;
|
||||
|
@ -429,7 +470,8 @@ void vector_buffer_impl(Class_&, std::false_type) {}
|
|||
|
||||
template <typename Vector, typename Class_, typename... Args>
|
||||
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)
|
||||
|
@ -444,7 +486,7 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
|
|||
// 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:
|
||||
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;
|
||||
|
||||
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
|
||||
detail::vector_accessor<Vector, Class_>(cl);
|
||||
|
||||
cl.def("__bool__",
|
||||
[](const Vector &v) -> bool {
|
||||
return !v.empty();
|
||||
},
|
||||
"Check whether the list is nonempty"
|
||||
);
|
||||
cl.def(
|
||||
"__bool__",
|
||||
[](const Vector &v) -> bool { return !v.empty(); },
|
||||
"Check whether the list is nonempty");
|
||||
|
||||
cl.def("__len__", &Vector::size);
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
// C++ style functions deprecated, leaving it here as an example
|
||||
cl.def(init<size_type>());
|
||||
|
@ -524,8 +561,6 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
|
|||
return cl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// std::map, std::unordered_map
|
||||
//
|
||||
|
@ -533,68 +568,88 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
|
|||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/* Fallback functions */
|
||||
template <typename, typename, typename... Args> void map_if_insertion_operator(const Args &...) { }
|
||||
template <typename, typename, typename... Args> void map_assignment(const Args &...) { }
|
||||
template <typename, typename, typename... 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
|
||||
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 MappedType = typename Map::mapped_type;
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Map &m, const KeyType &k, const MappedType &v) {
|
||||
cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
|
||||
auto it = m.find(k);
|
||||
if (it != m.end()) it->second = v;
|
||||
else m.emplace(k, v);
|
||||
if (it != m.end()) {
|
||||
it->second = 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
|
||||
// reinserting
|
||||
template <typename Map, typename Class_>
|
||||
void map_assignment(enable_if_t<
|
||||
!is_copy_assignable<typename Map::mapped_type>::value &&
|
||||
is_copy_constructible<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,
|
||||
Class_> &cl) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Map &m, const KeyType &k, const MappedType &v) {
|
||||
cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
|
||||
// We can't use m[k] = v; because value type might not be default constructable
|
||||
auto r = m.emplace(k, v);
|
||||
if (!r.second) {
|
||||
// value type is not copy assignable so the only way to insert it is to erase it first...
|
||||
// value type is not copy assignable so the only way to insert it is to erase it
|
||||
// first...
|
||||
m.erase(r.first);
|
||||
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)
|
||||
-> decltype(std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void()) {
|
||||
|
||||
cl.def("__repr__",
|
||||
cl.def(
|
||||
"__repr__",
|
||||
[name](Map &m) {
|
||||
std::ostringstream s;
|
||||
s << name << '{';
|
||||
bool f = false;
|
||||
for (auto const &kv : m) {
|
||||
if (f)
|
||||
if (f) {
|
||||
s << ", ";
|
||||
}
|
||||
s << kv.first << ": " << kv.second;
|
||||
f = true;
|
||||
}
|
||||
s << '}';
|
||||
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 ↦
|
||||
};
|
||||
|
||||
template <typename Map>
|
||||
struct values_view {
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template <typename Map>
|
||||
struct items_view {
|
||||
Map ↦
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
|
@ -602,12 +657,15 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename...
|
|||
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&...args) {
|
||||
using KeyType = typename Map::key_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>;
|
||||
|
||||
// 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
|
||||
// module-local.
|
||||
auto tinfo = detail::get_type_info(typeid(MappedType));
|
||||
auto *tinfo = detail::get_type_info(typeid(MappedType));
|
||||
bool local = !tinfo || tinfo->module_local;
|
||||
if (local) {
|
||||
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_<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<>());
|
||||
|
||||
// Register stream insertion operator (if possible)
|
||||
detail::map_if_insertion_operator<Map, Class_>(cl, name);
|
||||
|
||||
cl.def("__bool__",
|
||||
cl.def(
|
||||
"__bool__",
|
||||
[](const Map &m) -> bool { return !m.empty(); },
|
||||
"Check whether the map is nonempty"
|
||||
);
|
||||
"Check whether the map is nonempty");
|
||||
|
||||
cl.def("__iter__",
|
||||
cl.def(
|
||||
"__iter__",
|
||||
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
|
||||
);
|
||||
|
||||
cl.def("items",
|
||||
[](Map &m) { return make_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
cl.def(
|
||||
"keys",
|
||||
[](Map &m) { return KeysView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("__getitem__",
|
||||
cl.def(
|
||||
"values",
|
||||
[](Map &m) { return ValuesView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
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 & {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end())
|
||||
if (it == m.end()) {
|
||||
throw key_error();
|
||||
}
|
||||
return it->second;
|
||||
},
|
||||
return_value_policy::reference_internal // ref + keepalive
|
||||
);
|
||||
|
||||
cl.def("__contains__",
|
||||
[](Map &m, const KeyType &k) -> bool {
|
||||
cl.def("__contains__", [](Map &m, const KeyType &k) -> bool {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end())
|
||||
if (it == m.end()) {
|
||||
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
|
||||
detail::map_assignment<Map, Class_>(cl);
|
||||
|
||||
cl.def("__delitem__",
|
||||
[](Map &m, const KeyType &k) {
|
||||
cl.def("__delitem__", [](Map &m, const KeyType &k) {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end())
|
||||
if (it == m.end()) {
|
||||
throw key_error();
|
||||
m.erase(it);
|
||||
}
|
||||
);
|
||||
m.erase(it);
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"}
|
||||
)
|
|
@ -1,8 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ._version import version_info, __version__
|
||||
from .commands import get_include, get_cmake_dir
|
||||
|
||||
from ._version import __version__, version_info
|
||||
from .commands import get_cmake_dir, get_include
|
||||
|
||||
__all__ = (
|
||||
"version_info",
|
||||
|
|
|
@ -5,7 +5,7 @@ import argparse
|
|||
import sys
|
||||
import sysconfig
|
||||
|
||||
from .commands import get_include, get_cmake_dir
|
||||
from .commands import get_cmake_dir, get_include
|
||||
|
||||
|
||||
def print_includes():
|
||||
|
|
|
@ -8,5 +8,5 @@ def _to_int(s):
|
|||
return s
|
||||
|
||||
|
||||
__version__ = "2.7.1"
|
||||
__version__ = "2.9.2"
|
||||
version_info = tuple(_to_int(s) for s in __version__.split("."))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Union, Tuple
|
||||
from typing import Tuple, Union
|
||||
|
||||
def _to_int(s: str) -> Union[int, str]: ...
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
|
||||
|
||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
|
|
|
@ -41,23 +41,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
import contextlib
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import shutil
|
||||
import sys
|
||||
import sysconfig
|
||||
import tempfile
|
||||
import threading
|
||||
import platform
|
||||
import warnings
|
||||
import sysconfig
|
||||
|
||||
try:
|
||||
from setuptools.command.build_ext import build_ext as _build_ext
|
||||
from setuptools import Extension as _Extension
|
||||
from setuptools.command.build_ext import build_ext as _build_ext
|
||||
except ImportError:
|
||||
from distutils.command.build_ext import build_ext as _build_ext
|
||||
from distutils.extension import Extension as _Extension
|
||||
|
||||
import distutils.errors
|
||||
import distutils.ccompiler
|
||||
import distutils.errors
|
||||
|
||||
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
|
||||
PY2 = sys.version_info[0] < 3
|
||||
|
@ -143,7 +144,12 @@ class Pybind11Extension(_Extension):
|
|||
if WIN:
|
||||
cflags += ["/EHsc", "/bigobj"]
|
||||
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:
|
||||
cflags += ["-stdlib=libc++"]
|
||||
ldflags += ["-stdlib=libc++"]
|
||||
|
@ -460,8 +466,14 @@ class ParallelCompile(object):
|
|||
threads = 1
|
||||
|
||||
if threads > 1:
|
||||
for _ in ThreadPool(threads).imap_unordered(_single_compile, objects):
|
||||
pool = ThreadPool(threads)
|
||||
# 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:
|
||||
for ob in objects:
|
||||
_single_compile(ob)
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
|
||||
# pre-commit).
|
||||
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
|
||||
from types import TracebackType
|
||||
|
||||
import contextlib
|
||||
import distutils.ccompiler
|
||||
from distutils.command.build_ext import build_ext as _build_ext # type: ignore
|
||||
from distutils.extension import Extension as _Extension
|
||||
import distutils.ccompiler
|
||||
import contextlib
|
||||
from types import TracebackType
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
|
||||
|
||||
WIN: bool
|
||||
PY2: bool
|
||||
|
|
|
@ -1,3 +1,41 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=42", "wheel", "cmake>=3.18", "ninja"]
|
||||
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
|
||||
|
|
|
@ -20,6 +20,7 @@ classifiers =
|
|||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
License :: OSI Approved :: BSD License
|
||||
Programming Language :: Python :: Implementation :: PyPy
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
|
@ -30,24 +31,20 @@ keywords =
|
|||
C++11
|
||||
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]
|
||||
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
|
||||
|
||||
[bdist_wheel]
|
||||
universal=1
|
||||
|
||||
[check-manifest]
|
||||
ignore =
|
||||
tests/**
|
||||
docs/**
|
||||
tools/**
|
||||
include/**
|
||||
.*
|
||||
pybind11/include/**
|
||||
pybind11/share/**
|
||||
CMakeLists.txt
|
||||
|
||||
|
||||
[flake8]
|
||||
max-line-length = 99
|
||||
|
@ -61,25 +58,6 @@ ignore =
|
|||
# Black conflict
|
||||
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]
|
||||
timeout = 300
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# Setup script for PyPI; use CMakeFile.txt to build extension modules
|
||||
|
||||
import contextlib
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
@ -19,6 +20,39 @@ VERSION_REGEX = re.compile(
|
|||
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
|
||||
# files, and the sys.prefix files (CMake and headers).
|
||||
|
||||
|
@ -40,7 +74,7 @@ exec(code, loc)
|
|||
version = loc["__version__"]
|
||||
|
||||
# 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()))
|
||||
cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
|
||||
if version != cpp_version:
|
||||
|
@ -49,6 +83,15 @@ if version != cpp_version:
|
|||
)
|
||||
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):
|
||||
with open(filename, "rb" if binary else "r") as f:
|
||||
|
@ -106,6 +149,13 @@ with remove_output("pybind11/include", "pybind11/share"):
|
|||
"-DBUILD_TESTING=OFF",
|
||||
"-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)
|
||||
subprocess.check_call(cmd, **cmake_opts)
|
||||
subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts)
|
||||
|
|
|
@ -10,16 +10,16 @@ cmake_minimum_required(VERSION 3.4)
|
|||
# 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
|
||||
# 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})
|
||||
else()
|
||||
cmake_policy(VERSION 3.18)
|
||||
cmake_policy(VERSION 3.21)
|
||||
endif()
|
||||
|
||||
# Only needed for CMake < 3.5 support
|
||||
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:
|
||||
# pybind11_filter_tests(LISTNAME file1.cpp file2.cpp ... MESSAGE "")
|
||||
|
@ -27,10 +27,17 @@ include(CMakeParseArguments)
|
|||
macro(pybind11_filter_tests LISTNAME)
|
||||
cmake_parse_arguments(ARG "" "MESSAGE" "" ${ARGN})
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
endif()
|
||||
endforeach()
|
||||
|
@ -47,6 +54,18 @@ macro(possibly_uninitialized)
|
|||
endforeach()
|
||||
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
|
||||
if(DEFINED Python_EXECUTABLE)
|
||||
set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}")
|
||||
|
@ -92,52 +111,67 @@ if(PYBIND11_CUDA_TESTS)
|
|||
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
|
||||
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
|
||||
test_async.cpp
|
||||
test_buffers.cpp
|
||||
test_builtin_casters.cpp
|
||||
test_call_policies.cpp
|
||||
test_callbacks.cpp
|
||||
test_chrono.cpp
|
||||
test_class.cpp
|
||||
test_constants_and_functions.cpp
|
||||
test_copy_move.cpp
|
||||
test_custom_type_casters.cpp
|
||||
test_docstring_options.cpp
|
||||
test_eigen.cpp
|
||||
test_enum.cpp
|
||||
test_eval.cpp
|
||||
test_exceptions.cpp
|
||||
test_factory_constructors.cpp
|
||||
test_gil_scoped.cpp
|
||||
test_iostream.cpp
|
||||
test_kwargs_and_defaults.cpp
|
||||
test_local_bindings.cpp
|
||||
test_methods_and_attributes.cpp
|
||||
test_modules.cpp
|
||||
test_multiple_inheritance.cpp
|
||||
test_numpy_array.cpp
|
||||
test_numpy_dtypes.cpp
|
||||
test_numpy_vectorize.cpp
|
||||
test_opaque_types.cpp
|
||||
test_operator_overloading.cpp
|
||||
test_pickling.cpp
|
||||
test_pytypes.cpp
|
||||
test_sequences_and_iterators.cpp
|
||||
test_smart_ptr.cpp
|
||||
test_stl.cpp
|
||||
test_stl_binders.cpp
|
||||
test_tagbased_polymorphic.cpp
|
||||
test_union.cpp
|
||||
test_virtual_functions.cpp)
|
||||
test_async
|
||||
test_buffers
|
||||
test_builtin_casters
|
||||
test_call_policies
|
||||
test_callbacks
|
||||
test_chrono
|
||||
test_class
|
||||
test_const_name
|
||||
test_constants_and_functions
|
||||
test_copy_move
|
||||
test_custom_type_casters
|
||||
test_custom_type_setup
|
||||
test_docstring_options
|
||||
test_eigen
|
||||
test_enum
|
||||
test_eval
|
||||
test_exceptions
|
||||
test_factory_constructors
|
||||
test_gil_scoped
|
||||
test_iostream
|
||||
test_kwargs_and_defaults
|
||||
test_local_bindings
|
||||
test_methods_and_attributes
|
||||
test_modules
|
||||
test_multiple_inheritance
|
||||
test_numpy_array
|
||||
test_numpy_dtypes
|
||||
test_numpy_vectorize
|
||||
test_opaque_types
|
||||
test_operator_overloading
|
||||
test_pickling
|
||||
test_pytypes
|
||||
test_sequences_and_iterators
|
||||
test_smart_ptr
|
||||
test_stl
|
||||
test_stl_binders
|
||||
test_tagbased_polymorphic
|
||||
test_thread
|
||||
test_union
|
||||
test_virtual_functions)
|
||||
|
||||
# Invoking cmake with something like:
|
||||
# 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:
|
||||
# cmake -DPYBIND11_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()
|
||||
|
||||
# You can also filter tests:
|
||||
|
@ -159,15 +193,46 @@ if(PYBIND11_CUDA_TESTS)
|
|||
"Skipping test_constants_and_functions due to incompatible exception specifications")
|
||||
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
|
||||
# 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.
|
||||
set(PYBIND11_CROSS_MODULE_TESTS test_exceptions.py test_local_bindings.py test_stl.py
|
||||
test_stl_binders.py)
|
||||
tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.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
|
||||
# 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")
|
||||
endif()
|
||||
|
||||
set(EIGEN3_VERSION_STRING "3.3.8")
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
eigen
|
||||
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
|
||||
GIT_TAG ${EIGEN3_VERSION_STRING})
|
||||
GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
|
||||
GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}")
|
||||
|
||||
FetchContent_GetProperties(eigen)
|
||||
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)
|
||||
endif()
|
||||
|
||||
set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR})
|
||||
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()
|
||||
find_package(Eigen3 3.2.7 QUIET CONFIG)
|
||||
|
@ -248,7 +317,9 @@ if(Boost_FOUND)
|
|||
endif()
|
||||
|
||||
# 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)
|
||||
else()
|
||||
file(
|
||||
|
@ -278,7 +349,7 @@ elseif(${STD_FS_NEEDS_CXXFS})
|
|||
elseif(${STD_FS_NO_LIB_NEEDED})
|
||||
set(STD_FS_LIB "")
|
||||
else()
|
||||
message(WARNING "Unknown compiler - not passing -lstdc++fs")
|
||||
message(WARNING "Unknown C++17 compiler - not passing -lstdc++fs")
|
||||
set(STD_FS_LIB "")
|
||||
endif()
|
||||
|
||||
|
@ -306,6 +377,9 @@ function(pybind11_enable_warnings target_name)
|
|||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang|IntelLLVM)")
|
||||
target_compile_options(${target_name} PRIVATE -Werror)
|
||||
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_name}
|
||||
PRIVATE
|
||||
|
@ -329,21 +403,17 @@ endfunction()
|
|||
|
||||
set(test_targets pybind11_tests)
|
||||
|
||||
# Build pybind11_cross_module_tests if any test_whatever.py are being built that require it
|
||||
foreach(t ${PYBIND11_CROSS_MODULE_TESTS})
|
||||
list(FIND PYBIND11_PYTEST_FILES ${t} i)
|
||||
if(i GREATER -1)
|
||||
list(APPEND test_targets pybind11_cross_module_tests)
|
||||
break()
|
||||
# Check if any tests need extra targets by iterating through the mappings registered.
|
||||
foreach(i ${PYBIND11_TEST_EXTRA_TARGETS})
|
||||
foreach(needle ${PYBIND11_TEST_EXTRA_TARGETS_NEEDLES_${i}})
|
||||
if(needle IN_LIST PYBIND11_PYTEST_FILES)
|
||||
# Add all the additional targets to the test list. List join in newer cmake.
|
||||
foreach(extra_target ${PYBIND11_TEST_EXTRA_TARGETS_ADDITION_${i}})
|
||||
list(APPEND test_targets ${extra_target})
|
||||
endforeach()
|
||||
break() # Breaks out of the needle search, continues with the next mapping.
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(t ${PYBIND11_CROSS_MODULE_GIL_TESTS})
|
||||
list(FIND PYBIND11_PYTEST_FILES ${t} i)
|
||||
if(i GREATER -1)
|
||||
list(APPEND test_targets cross_module_gil_utils)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Support CUDA testing by forcing the target file to compile with NVCC
|
||||
|
@ -409,6 +479,14 @@ foreach(target ${test_targets})
|
|||
endif()
|
||||
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
|
||||
pybind11_find_import(pytest VERSION 3.1)
|
||||
|
||||
|
|
|
@ -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
|
||||
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
|
||||
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 <unordered_map>
|
||||
|
||||
#include <list>
|
||||
#include <typeindex>
|
||||
#include <sstream>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
|
||||
class ConstructorStats {
|
||||
protected:
|
||||
std::unordered_map<void*, int> _instances; // Need a map rather than set because members can shared address with parents
|
||||
std::list<std::string> _values; // Used to track values (e.g. of value constructors)
|
||||
std::unordered_map<void *, int> _instances; // Need a map rather than set because members can
|
||||
// shared address with parents
|
||||
std::list<std::string> _values; // Used to track values
|
||||
// (e.g. of value constructors)
|
||||
public:
|
||||
int default_constructions = 0;
|
||||
int copy_constructions = 0;
|
||||
|
@ -96,26 +100,26 @@ public:
|
|||
default_constructions++;
|
||||
}
|
||||
|
||||
void created(void *inst) {
|
||||
++_instances[inst];
|
||||
}
|
||||
void created(void *inst) { ++_instances[inst]; }
|
||||
|
||||
void destroyed(void *inst) {
|
||||
if (--_instances[inst] < 0)
|
||||
if (--_instances[inst] < 0) {
|
||||
throw std::runtime_error("cstats.destroyed() called with unknown "
|
||||
"instance; potential double-destruction "
|
||||
"or a missing cstats.created()");
|
||||
}
|
||||
}
|
||||
|
||||
static void gc() {
|
||||
// Force garbage collection to ensure any pending destructors are invoked:
|
||||
#if defined(PYPY_VERSION)
|
||||
PyObject *globals = PyEval_GetGlobals();
|
||||
PyObject *result = PyRun_String(
|
||||
"import gc\n"
|
||||
PyObject *result = PyRun_String("import gc\n"
|
||||
"for i in range(2):"
|
||||
" gc.collect()\n",
|
||||
Py_file_input, globals, globals);
|
||||
Py_file_input,
|
||||
globals,
|
||||
globals);
|
||||
if (result == nullptr)
|
||||
throw py::error_already_set();
|
||||
Py_DECREF(result);
|
||||
|
@ -127,15 +131,18 @@ public:
|
|||
int alive() {
|
||||
gc();
|
||||
int total = 0;
|
||||
for (const auto &p : _instances)
|
||||
if (p.second > 0)
|
||||
for (const auto &p : _instances) {
|
||||
if (p.second > 0) {
|
||||
total += p.second;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void value() {} // Recursion terminator
|
||||
// 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;
|
||||
oss << v;
|
||||
_values.push_back(oss.str());
|
||||
|
@ -145,7 +152,9 @@ public:
|
|||
// Move out stored values
|
||||
py::list values() {
|
||||
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();
|
||||
return l;
|
||||
}
|
||||
|
@ -157,7 +166,8 @@ public:
|
|||
}
|
||||
|
||||
// Gets constructor stats from a C++ type
|
||||
template <typename T> static ConstructorStats& get() {
|
||||
template <typename T>
|
||||
static ConstructorStats &get() {
|
||||
#if defined(PYPY_VERSION)
|
||||
gc();
|
||||
#endif
|
||||
|
@ -169,7 +179,8 @@ public:
|
|||
auto &internals = py::detail::get_internals();
|
||||
const std::type_index *t1 = nullptr, *t2 = nullptr;
|
||||
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) {
|
||||
if (p.second == type_info) {
|
||||
if (t1) {
|
||||
|
@ -179,17 +190,23 @@ public:
|
|||
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);
|
||||
// If we have both a t1 and t2 match, one is probably the trampoline class; return whichever
|
||||
// has more constructions (typically one or the other will be 0)
|
||||
// If we have both a t1 and t2 match, one is probably the trampoline class; return
|
||||
// whichever has more constructions (typically one or the other will be 0)
|
||||
if (t2) {
|
||||
auto &cs2 = get(*t2);
|
||||
int cs1_total = cs1.default_constructions + cs1.copy_constructions + cs1.move_constructions + (int) cs1._values.size();
|
||||
int cs2_total = cs2.default_constructions + cs2.copy_constructions + cs2.move_constructions + (int) cs2._values.size();
|
||||
if (cs2_total > cs1_total) return cs2;
|
||||
int cs1_total = cs1.default_constructions + cs1.copy_constructions
|
||||
+ cs1.move_constructions + (int) cs1._values.size();
|
||||
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;
|
||||
}
|
||||
|
@ -198,78 +215,108 @@ public:
|
|||
// 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
|
||||
// constructor stats values for later inspection.
|
||||
template <class T> void track_copy_created(T *inst) { 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) {
|
||||
template <class T>
|
||||
void track_copy_created(T *inst) {
|
||||
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>();
|
||||
cst.copy_assignments++;
|
||||
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>();
|
||||
cst.move_assignments++;
|
||||
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>();
|
||||
cst.default_created(inst);
|
||||
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>();
|
||||
cst.created(inst);
|
||||
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);
|
||||
}
|
||||
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)...);
|
||||
}
|
||||
|
||||
/// Don't cast pointers to Python, print them as strings
|
||||
inline const char *format_ptrs(const char *p) { return p; }
|
||||
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>
|
||||
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>
|
||||
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))...);
|
||||
}
|
||||
|
||||
// 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...);
|
||||
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...);
|
||||
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...);
|
||||
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...);
|
||||
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...);
|
||||
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...);
|
||||
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...);
|
||||
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...);
|
||||
track_values(inst, values...);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// This file mimics a DSO that makes pybind11 calls but does not define a
|
||||
|
@ -25,30 +26,21 @@ void gil_acquire() { py::gil_scoped_acquire gil; }
|
|||
constexpr char kModuleName[] = "cross_module_gil_utils";
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
kModuleName,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
struct PyModuleDef moduledef
|
||||
= {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL};
|
||||
#else
|
||||
PyMethodDef module_methods[] = {
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
PyMethodDef module_methods[] = {{NULL, NULL, 0, NULL}};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" PYBIND11_EXPORT
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject* PyInit_cross_module_gil_utils()
|
||||
PyObject *
|
||||
PyInit_cross_module_gil_utils()
|
||||
#else
|
||||
void initcross_module_gil_utils()
|
||||
void
|
||||
initcross_module_gil_utils()
|
||||
#endif
|
||||
{
|
||||
|
||||
|
@ -60,11 +52,10 @@ void initcross_module_gil_utils()
|
|||
#endif
|
||||
|
||||
if (m != NULL) {
|
||||
static_assert(
|
||||
sizeof(&gil_acquire) == sizeof(void*),
|
||||
static_assert(sizeof(&gil_acquire) == sizeof(void *),
|
||||
"Function pointer must have the same size as void*");
|
||||
PyModule_AddObject(m, "gil_acquire_funcaddr",
|
||||
PyLong_FromVoidPtr(reinterpret_cast<void*>(&gil_acquire)));
|
||||
PyModule_AddObject(
|
||||
m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire)));
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
|
||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
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])
|
||||
|
@ -71,13 +72,20 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
|
|||
encoding="ascii",
|
||||
)
|
||||
|
||||
subprocess.check_call(
|
||||
out = subprocess.check_output(
|
||||
[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
|
||||
print(out)
|
||||
for item in tmpdir.listdir():
|
||||
print(item.basename)
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#pragma once
|
||||
#include <utility>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
/// Simple class used to test py::local:
|
||||
template <int> class LocalBase {
|
||||
template <int>
|
||||
class LocalBase {
|
||||
public:
|
||||
LocalBase(int i) : i(i) { }
|
||||
explicit LocalBase(int i) : i(i) {}
|
||||
int i = -1;
|
||||
};
|
||||
|
||||
|
@ -35,6 +36,26 @@ using NonLocalVec2 = std::vector<NonLocal2>;
|
|||
using NonLocalMap = std::unordered_map<std::string, NonLocalType>;
|
||||
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(LocalVec2);
|
||||
PYBIND11_MAKE_OPAQUE(LocalMap);
|
||||
|
@ -43,24 +64,29 @@ PYBIND11_MAKE_OPAQUE(NonLocalVec);
|
|||
PYBIND11_MAKE_OPAQUE(NonLocalMap);
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalMap2);
|
||||
|
||||
|
||||
// Simple bindings (used with the above):
|
||||
template <typename T, int Adjust = 0, typename... Args>
|
||||
py::class_<T> bind_local(Args &&...args) {
|
||||
return py::class_<T>(std::forward<Args>(args)...)
|
||||
.def(py::init<int>())
|
||||
.def("get", [](T &i) { return i.i + Adjust; });
|
||||
return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) {
|
||||
return i.i + Adjust;
|
||||
});
|
||||
};
|
||||
|
||||
// Simulate a foreign library base class (to match the example in the docs):
|
||||
namespace pets {
|
||||
class Pet {
|
||||
public:
|
||||
Pet(std::string name) : name_(std::move(name)) {}
|
||||
explicit Pet(std::string name) : name_(std::move(name)) {}
|
||||
std::string name_;
|
||||
const std::string &name() const { return name_; }
|
||||
};
|
||||
} // namespace pets
|
||||
|
||||
struct MixGL { int i; MixGL(int i) : i{i} {} };
|
||||
struct MixGL2 { int i; MixGL2(int i) : i{i} {} };
|
||||
struct MixGL {
|
||||
int i;
|
||||
explicit MixGL(int i) : i{i} {}
|
||||
};
|
||||
struct MixGL2 {
|
||||
int i;
|
||||
explicit MixGL2(int i) : i{i} {}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#if !defined(__OBJECT_H)
|
||||
# define __OBJECT_H
|
||||
|
||||
#include <atomic>
|
||||
# include "constructor_stats.h"
|
||||
|
||||
# include <atomic>
|
||||
|
||||
/// Reference counted object base class
|
||||
class Object {
|
||||
public:
|
||||
|
@ -27,18 +28,21 @@ public:
|
|||
*/
|
||||
void decRef(bool dealloc = true) const {
|
||||
--m_refCount;
|
||||
if (m_refCount == 0 && dealloc)
|
||||
if (m_refCount == 0 && dealloc) {
|
||||
delete this;
|
||||
else if (m_refCount < 0)
|
||||
} else if (m_refCount < 0) {
|
||||
throw std::runtime_error("Internal error: reference count < 0!");
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
protected:
|
||||
/** \brief Virtual protected deconstructor.
|
||||
* (Will only be called by \ref ref)
|
||||
*/
|
||||
virtual ~Object() { print_destroyed(this); }
|
||||
|
||||
private:
|
||||
mutable std::atomic<int> m_refCount{0};
|
||||
};
|
||||
|
@ -59,50 +63,64 @@ class ref_tag {};
|
|||
*
|
||||
* \ingroup libcore
|
||||
*/
|
||||
template <typename T> class ref {
|
||||
template <typename T>
|
||||
class ref {
|
||||
public:
|
||||
/// 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
|
||||
ref(T *ptr) : m_ptr(ptr) {
|
||||
if (m_ptr) ((Object *) m_ptr)->incRef();
|
||||
|
||||
print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer");
|
||||
explicit ref(T *ptr) : m_ptr(ptr) {
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->incRef();
|
||||
}
|
||||
|
||||
print_created(this, "from pointer", m_ptr);
|
||||
track_created((ref_tag *) this, "from pointer");
|
||||
}
|
||||
|
||||
/// Copy constructor
|
||||
ref(const ref &r) : m_ptr(r.m_ptr) {
|
||||
if (m_ptr)
|
||||
if (m_ptr) {
|
||||
((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
|
||||
ref(ref &&r) noexcept : m_ptr(r.m_ptr) {
|
||||
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
|
||||
~ref() {
|
||||
if (m_ptr)
|
||||
if (m_ptr) {
|
||||
((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
|
||||
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;
|
||||
if (m_ptr)
|
||||
}
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->decRef();
|
||||
}
|
||||
m_ptr = r.m_ptr;
|
||||
r.m_ptr = nullptr;
|
||||
return *this;
|
||||
|
@ -110,29 +128,40 @@ public:
|
|||
|
||||
/// Overwrite this reference with another reference
|
||||
ref &operator=(const ref &r) {
|
||||
print_copy_assigned(this, "pointer", r.m_ptr); track_copy_assigned((ref_tag*) this);
|
||||
|
||||
if (m_ptr == r.m_ptr)
|
||||
if (this == &r) {
|
||||
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();
|
||||
}
|
||||
m_ptr = r.m_ptr;
|
||||
if (m_ptr)
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->incRef();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Overwrite this reference with a pointer to another object
|
||||
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;
|
||||
if (m_ptr)
|
||||
}
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->decRef();
|
||||
}
|
||||
m_ptr = ptr;
|
||||
if (m_ptr)
|
||||
if (m_ptr) {
|
||||
((Object *) m_ptr)->incRef();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -161,13 +190,14 @@ public:
|
|||
const T &operator*() const { return *m_ptr; }
|
||||
|
||||
/// 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
|
||||
T *get_ptr() { return m_ptr; }
|
||||
|
||||
/// Return a pointer to the referenced object
|
||||
const T *get_ptr() const { return m_ptr; }
|
||||
|
||||
private:
|
||||
T *m_ptr;
|
||||
};
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
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 "local_bindings.h"
|
||||
#include "pybind11_tests.h"
|
||||
#include "test_exceptions.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
|
@ -29,24 +29,46 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
|||
bind_local<ExternalType2>(m, "ExternalType2", py::module_local());
|
||||
|
||||
// test_exceptions.py
|
||||
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(); });
|
||||
py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException");
|
||||
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_type_error", []() { throw py::type_error("pybind11 type error"); });
|
||||
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) {
|
||||
try {
|
||||
if (p) std::rethrow_exception(p);
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} 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
|
||||
// Local to both:
|
||||
bind_local<LocalType, 1>(m, "LocalType", py::module_local())
|
||||
.def("get2", [](LocalType &t) { return t.i + 2; })
|
||||
;
|
||||
bind_local<LocalType, 1>(m, "LocalType", py::module_local()).def("get2", [](LocalType &t) {
|
||||
return t.i + 2;
|
||||
});
|
||||
|
||||
// Can only be called with our python type:
|
||||
m.def("local_value", [](LocalType &l) { return l.i; });
|
||||
|
@ -54,9 +76,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
|||
// test_nonlocal_failure
|
||||
// This registration will fail (global registration when LocalFail is already registered
|
||||
// globally in the main test module):
|
||||
m.def("register_nonlocal", [m]() {
|
||||
bind_local<NonLocalType, 0>(m, "NonLocalType");
|
||||
});
|
||||
m.def("register_nonlocal", [m]() { bind_local<NonLocalType, 0>(m, "NonLocalType"); });
|
||||
|
||||
// test_stl_bind_local
|
||||
// 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
|
||||
// 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):
|
||||
m.def("register_nonlocal_vec", [m]() {
|
||||
py::bind_vector<NonLocalVec>(m, "NonLocalVec");
|
||||
});
|
||||
m.def("register_nonlocal_map", [m]() {
|
||||
py::bind_map<NonLocalMap>(m, "NonLocalMap");
|
||||
});
|
||||
m.def("register_nonlocal_vec", [m]() { py::bind_vector<NonLocalVec>(m, "NonLocalVec"); });
|
||||
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
|
||||
// `py::module_local(false)`.
|
||||
// Explicitly made local:
|
||||
py::bind_vector<NonLocalVec2>(m, "NonLocalVec2", py::module_local());
|
||||
// Explicitly made global (and so will fail to bind):
|
||||
m.def("register_nonlocal_map2", [m]() {
|
||||
py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false));
|
||||
});
|
||||
m.def("register_nonlocal_map2",
|
||||
[m]() { py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false)); });
|
||||
|
||||
// test_mixed_local_global
|
||||
// We try this both with the global type registered first and vice versa (the order shouldn't
|
||||
// matter).
|
||||
m.def("register_mixed_global_local", [m]() {
|
||||
bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local());
|
||||
});
|
||||
m.def("register_mixed_global_local",
|
||||
[m]() { bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local()); });
|
||||
m.def("register_mixed_local_global", [m]() {
|
||||
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); });
|
||||
|
||||
// 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
|
||||
py::bind_vector<std::vector<int>>(m, "VectorInt");
|
||||
|
||||
m.def("load_vector_via_binding", [](std::vector<int> &v) {
|
||||
return std::accumulate(v.begin(), v.end(), 0);
|
||||
});
|
||||
m.def("load_vector_via_binding",
|
||||
[](std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); });
|
||||
|
||||
// test_cross_module_calls
|
||||
m.def("return_self", [](LocalVec *v) { return v; });
|
||||
|
@ -109,13 +123,11 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
|||
|
||||
class Dog : public pets::Pet {
|
||||
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())
|
||||
.def("name", &pets::Pet::name);
|
||||
py::class_<pets::Pet>(m, "Pet", py::module_local()).def("name", &pets::Pet::name);
|
||||
// Binding for local extending class:
|
||||
py::class_<Dog, pets::Pet>(m, "Dog")
|
||||
.def(py::init<std::string>());
|
||||
py::class_<Dog, pets::Pet>(m, "Dog").def(py::init<std::string>());
|
||||
m.def("pet_name", [](pets::Pet &p) { return p.name(); });
|
||||
|
||||
py::class_<MixGL>(m, "MixGL", py::module_local()).def(py::init<int>());
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include "constructor_stats.h"
|
||||
|
||||
#include <functional>
|
||||
|
@ -31,9 +32,7 @@ std::list<std::function<void(py::module_ &)>> &initializers() {
|
|||
return inits;
|
||||
}
|
||||
|
||||
test_initializer::test_initializer(Initializer init) {
|
||||
initializers().emplace_back(init);
|
||||
}
|
||||
test_initializer::test_initializer(Initializer init) { initializers().emplace_back(init); }
|
||||
|
||||
test_initializer::test_initializer(const char *submodule_name, Initializer init) {
|
||||
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("copy_constructions", &ConstructorStats::copy_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
|
||||
// to allow instance cleanup checks (invokes a GC first)
|
||||
// Not exactly ConstructorStats, but related: expose the internal pybind number of
|
||||
// registered instances to allow instance cleanup checks (invokes a GC first)
|
||||
.def_static("detail_reg_inst", []() {
|
||||
ConstructorStats::gc();
|
||||
return py::detail::get_internals().registered_instances.size();
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(pybind11_tests, m) {
|
||||
|
@ -86,6 +86,7 @@ PYBIND11_MODULE(pybind11_tests, m) {
|
|||
.def(py::init<int>())
|
||||
.def("__repr__", [](const IncType &u) { return "IncType({})"_s.format(u.value()); });
|
||||
|
||||
for (const auto &initializer : initializers())
|
||||
for (const auto &initializer : initializers()) {
|
||||
initializer(m);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
#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/pybind11.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
// We get some really long type names here which causes MSVC 2015 to emit warnings
|
||||
# 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
|
||||
|
||||
namespace py = pybind11;
|
||||
|
@ -19,15 +16,14 @@ class test_initializer {
|
|||
using Initializer = void (*)(py::module_ &);
|
||||
|
||||
public:
|
||||
test_initializer(Initializer init);
|
||||
explicit test_initializer(Initializer init);
|
||||
test_initializer(const char *submodule_name, Initializer init);
|
||||
};
|
||||
|
||||
#define TEST_SUBMODULE(name, variable) \
|
||||
void test_submodule_##name(py::module_ &); \
|
||||
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
|
||||
struct UnregisteredType {};
|
||||
|
@ -36,7 +32,7 @@ struct UnregisteredType { };
|
|||
class UserType {
|
||||
public:
|
||||
UserType() = default;
|
||||
UserType(int i) : i(i) { }
|
||||
explicit UserType(int i) : i(i) {}
|
||||
|
||||
int value() const { return i; }
|
||||
void set(int set) { i = set; }
|
||||
|
@ -62,16 +58,21 @@ union IntFloat {
|
|||
float f;
|
||||
};
|
||||
|
||||
/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast context.
|
||||
/// Used to test recursive casters (e.g. std::tuple, stl containers).
|
||||
/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast
|
||||
/// context. Used to test recursive casters (e.g. std::tuple, stl containers).
|
||||
struct RValueCaster {};
|
||||
PYBIND11_NAMESPACE_BEGIN(pybind11)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template<> class type_caster<RValueCaster> {
|
||||
template <>
|
||||
class type_caster<RValueCaster> {
|
||||
public:
|
||||
PYBIND11_TYPE_CASTER(RValueCaster, _x("RValueCaster"));
|
||||
static handle cast(RValueCaster &&, return_value_policy, handle) { return py::str("rvalue").release(); }
|
||||
static handle cast(const RValueCaster &, return_value_policy, handle) { return py::str("lvalue").release(); }
|
||||
PYBIND11_TYPE_CASTER(RValueCaster, const_name("RValueCaster"));
|
||||
static handle cast(RValueCaster &&, return_value_policy, handle) {
|
||||
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(pybind11)
|
||||
|
@ -85,5 +86,6 @@ void ignoreOldStyleInitWarnings(F &&body) {
|
|||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore", message=message, category=FutureWarning)
|
||||
body()
|
||||
)", py::dict(py::arg("body") = py::cpp_function(body)));
|
||||
)",
|
||||
py::dict(py::arg("body") = py::cpp_function(body)));
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
numpy==1.18.0; platform_python_implementation=="PyPy" and sys_platform=="darwin" and python_version>="3.6"
|
||||
numpy==1.19.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") 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.16.6; python_version<"3.6" and sys_platform!="win32" and platform_python_implementation!="PyPy"
|
||||
numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" 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" and python_version=="3.6"
|
||||
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==6.1.2; python_version=="3.5"
|
||||
pytest==6.2.1; python_version>="3.6" and python_version<="3.9"
|
||||
pytest @ git+https://github.com/pytest-dev/pytest@c117bc350ec1e570672fda3b2ad234fd52e72b53; python_version>="3.10"
|
||||
pytest==6.2.4; python_version>="3.6"
|
||||
pytest-timeout
|
||||
scipy==1.2.3; (platform_python_implementation!="PyPy" or sys_platform=="linux") 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.2.3; platform_python_implementation!="PyPy" and python_version<"3.6"
|
||||
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10"
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
|
||||
TEST_SUBMODULE(async_module, m) {
|
||||
struct DoesNotSupportAsync {};
|
||||
py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync")
|
||||
.def(py::init<>());
|
||||
py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync").def(py::init<>());
|
||||
struct SupportsAsync {};
|
||||
py::class_<SupportsAsync>(m, "SupportsAsync")
|
||||
.def(py::init<>())
|
||||
|
|
|
@ -7,22 +7,26 @@
|
|||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
TEST_SUBMODULE(buffers, m) {
|
||||
// test_from_python / test_to_python:
|
||||
class Matrix {
|
||||
public:
|
||||
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");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
m_data = new 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) {
|
||||
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)];
|
||||
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
|
||||
}
|
||||
|
@ -35,12 +39,17 @@ TEST_SUBMODULE(buffers, m) {
|
|||
}
|
||||
|
||||
~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;
|
||||
}
|
||||
|
||||
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;
|
||||
m_rows = s.m_rows;
|
||||
m_cols = s.m_cols;
|
||||
|
@ -50,11 +59,16 @@ TEST_SUBMODULE(buffers, m) {
|
|||
}
|
||||
|
||||
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) {
|
||||
delete[] m_data;
|
||||
m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data;
|
||||
s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr;
|
||||
m_rows = s.m_rows;
|
||||
m_cols = s.m_cols;
|
||||
m_data = s.m_data;
|
||||
s.m_rows = 0;
|
||||
s.m_cols = 0;
|
||||
s.m_data = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -71,6 +85,7 @@ TEST_SUBMODULE(buffers, m) {
|
|||
|
||||
py::ssize_t rows() const { return m_rows; }
|
||||
py::ssize_t cols() const { return m_cols; }
|
||||
|
||||
private:
|
||||
py::ssize_t m_rows;
|
||||
py::ssize_t m_cols;
|
||||
|
@ -81,10 +96,11 @@ TEST_SUBMODULE(buffers, m) {
|
|||
/// Construct from a buffer
|
||||
.def(py::init([](const py::buffer &b) {
|
||||
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!");
|
||||
}
|
||||
|
||||
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()));
|
||||
return v;
|
||||
}))
|
||||
|
@ -95,14 +111,16 @@ TEST_SUBMODULE(buffers, m) {
|
|||
/// Bare bones interface
|
||||
.def("__getitem__",
|
||||
[](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();
|
||||
}
|
||||
return m(i.first, i.second);
|
||||
})
|
||||
.def("__setitem__",
|
||||
[](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();
|
||||
}
|
||||
m(i.first, i.second) = v;
|
||||
})
|
||||
/// Provide buffer access
|
||||
|
@ -111,19 +129,16 @@ TEST_SUBMODULE(buffers, m) {
|
|||
m.data(), /* Pointer to buffer */
|
||||
{m.rows(), m.cols()}, /* Buffer dimensions */
|
||||
{sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
|
||||
sizeof(float) }
|
||||
);
|
||||
sizeof(float)});
|
||||
});
|
||||
|
||||
// test_inherited_protocol
|
||||
class SquareMatrix : public Matrix {
|
||||
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
|
||||
py::class_<SquareMatrix, Matrix>(m, "SquareMatrix")
|
||||
.def(py::init<py::ssize_t>());
|
||||
|
||||
py::class_<SquareMatrix, Matrix>(m, "SquareMatrix").def(py::init<py::ssize_t>());
|
||||
|
||||
// test_pointer_to_member_fn
|
||||
// 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;
|
||||
|
||||
py::buffer_info get_buffer_info() {
|
||||
return py::buffer_info(&value, sizeof(value),
|
||||
py::format_descriptor<int32_t>::format(), 1);
|
||||
return py::buffer_info(
|
||||
&value, sizeof(value), py::format_descriptor<int32_t>::format(), 1);
|
||||
}
|
||||
};
|
||||
py::class_<Buffer>(m, "Buffer", py::buffer_protocol())
|
||||
|
@ -141,7 +156,6 @@ TEST_SUBMODULE(buffers, m) {
|
|||
.def_readwrite("value", &Buffer::value)
|
||||
.def_buffer(&Buffer::get_buffer_info);
|
||||
|
||||
|
||||
class ConstBuffer {
|
||||
std::unique_ptr<int32_t> value;
|
||||
|
||||
|
@ -150,8 +164,8 @@ TEST_SUBMODULE(buffers, m) {
|
|||
void set_value(int32_t v) { *value = v; }
|
||||
|
||||
py::buffer_info get_buffer_info() const {
|
||||
return py::buffer_info(value.get(), sizeof(*value),
|
||||
py::format_descriptor<int32_t>::format(), 1);
|
||||
return py::buffer_info(
|
||||
value.get(), sizeof(*value), py::format_descriptor<int32_t>::format(), 1);
|
||||
}
|
||||
|
||||
ConstBuffer() : value(new int32_t{0}) {}
|
||||
|
@ -169,11 +183,9 @@ TEST_SUBMODULE(buffers, m) {
|
|||
|
||||
struct BufferReadOnly {
|
||||
const uint8_t value = 0;
|
||||
BufferReadOnly(uint8_t value): value(value) {}
|
||||
explicit BufferReadOnly(uint8_t value) : value(value) {}
|
||||
|
||||
py::buffer_info get_buffer_info() {
|
||||
return py::buffer_info(&value, 1);
|
||||
}
|
||||
py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1); }
|
||||
};
|
||||
py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol())
|
||||
.def(py::init<uint8_t>())
|
||||
|
@ -183,9 +195,7 @@ TEST_SUBMODULE(buffers, m) {
|
|||
uint8_t value = 0;
|
||||
bool readonly = false;
|
||||
|
||||
py::buffer_info get_buffer_info() {
|
||||
return py::buffer_info(&value, 1, readonly);
|
||||
}
|
||||
py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1, readonly); }
|
||||
};
|
||||
py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol())
|
||||
.def(py::init<>())
|
||||
|
@ -204,9 +214,11 @@ TEST_SUBMODULE(buffers, m) {
|
|||
.def_readonly("strides", &py::buffer_info::strides)
|
||||
.def_readonly("readonly", &py::buffer_info::readonly)
|
||||
.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(); });
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import ctypes
|
||||
import io
|
||||
import struct
|
||||
import ctypes
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
|
||||
from pybind11_tests import buffers as m
|
||||
import env
|
||||
from pybind11_tests import ConstructorStats
|
||||
from pybind11_tests import buffers as m
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
|
@ -37,6 +36,10 @@ def test_from_python():
|
|||
|
||||
|
||||
# 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():
|
||||
mat = m.Matrix(5, 4)
|
||||
assert memoryview(mat).shape == (5, 4)
|
||||
|
|
|
@ -7,13 +7,9 @@
|
|||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/complex.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
#endif
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
struct ConstRefCasted {
|
||||
int tag;
|
||||
|
@ -24,36 +20,50 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
|||
template <>
|
||||
class type_caster<ConstRefCasted> {
|
||||
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
|
||||
// cast operator.
|
||||
bool load(handle, bool) { return true; }
|
||||
|
||||
operator ConstRefCasted &&() {
|
||||
explicit operator ConstRefCasted &&() {
|
||||
value = {1};
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
return std::move(value);
|
||||
}
|
||||
operator ConstRefCasted&() { value = {2}; return value; }
|
||||
operator ConstRefCasted*() { value = {3}; return &value; }
|
||||
explicit operator ConstRefCasted &() {
|
||||
value = {2};
|
||||
return value;
|
||||
}
|
||||
explicit operator ConstRefCasted *() {
|
||||
value = {3};
|
||||
return &value;
|
||||
}
|
||||
|
||||
operator const ConstRefCasted&() { value = {4}; return value; }
|
||||
operator const ConstRefCasted*() { value = {5}; return &value; }
|
||||
explicit operator const ConstRefCasted &() {
|
||||
value = {4};
|
||||
return value;
|
||||
}
|
||||
explicit operator const ConstRefCasted *() {
|
||||
value = {5};
|
||||
return &value;
|
||||
}
|
||||
|
||||
// custom cast_op to explicitly propagate types to the conversion operators.
|
||||
template <typename T_>
|
||||
using cast_op_type =
|
||||
/// const
|
||||
conditional_t<
|
||||
std::is_same<remove_reference_t<T_>, const ConstRefCasted*>::value, const ConstRefCasted*,
|
||||
std::is_same<remove_reference_t<T_>, const ConstRefCasted *>::value,
|
||||
const ConstRefCasted *,
|
||||
conditional_t<
|
||||
std::is_same<T_, const ConstRefCasted&>::value, const ConstRefCasted&,
|
||||
std::is_same<T_, const ConstRefCasted &>::value,
|
||||
const ConstRefCasted &,
|
||||
/// non-const
|
||||
conditional_t<
|
||||
std::is_same<remove_reference_t<T_>, ConstRefCasted*>::value, ConstRefCasted*,
|
||||
conditional_t<
|
||||
std::is_same<T_, ConstRefCasted&>::value, ConstRefCasted&,
|
||||
conditional_t<std::is_same<remove_reference_t<T_>, ConstRefCasted *>::value,
|
||||
ConstRefCasted *,
|
||||
conditional_t<std::is_same<T_, ConstRefCasted &>::value,
|
||||
ConstRefCasted &,
|
||||
/* else */ ConstRefCasted &&>>>>;
|
||||
|
||||
private:
|
||||
|
@ -67,27 +77,49 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
m.def("string_roundtrip", [](const char *s) { return s; });
|
||||
|
||||
// test_unicode_conversion
|
||||
// Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte
|
||||
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;
|
||||
// Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null
|
||||
// byte
|
||||
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;
|
||||
wstr.push_back(0x61); // a
|
||||
wstr.push_back(0x2e18); // ⸘
|
||||
if (sizeof(wchar_t) == 2) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16
|
||||
else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
|
||||
wstr.push_back(mathbfA16_1);
|
||||
wstr.push_back(mathbfA16_2);
|
||||
} // 𝐀, utf16
|
||||
else {
|
||||
wstr.push_back((wchar_t) mathbfA32);
|
||||
} // 𝐀, utf32
|
||||
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_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_utf8_string", []() {
|
||||
return std::string((const char *) u8"Say utf8\u203d \U0001f382 \U0001d400");
|
||||
}); // 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("bad_utf8_string", []() { return std::string("abc\xd0" "def"); });
|
||||
m.def("bad_utf8_string", []() {
|
||||
return std::string("abc\xd0"
|
||||
"def");
|
||||
});
|
||||
m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); });
|
||||
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError
|
||||
if (PY_MAJOR_VERSION >= 3)
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// 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 (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2)
|
||||
m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); });
|
||||
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_eacute", []() -> char { return '\xe9'; });
|
||||
m.def("u16_ibang", [=]() -> char16_t { return ib16; });
|
||||
|
@ -109,8 +141,13 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
|
||||
#ifdef PYBIND11_HAS_U8STRING
|
||||
m.attr("has_u8string") = true;
|
||||
m.def("good_utf8_u8string", []() { 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("good_utf8_u8string", []() {
|
||||
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'; });
|
||||
|
||||
|
@ -125,18 +162,71 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
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_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_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"); });
|
||||
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_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"); });
|
||||
|
||||
// The inner lambdas here are to also test implicit conversion
|
||||
using namespace std::literals;
|
||||
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_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_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
|
||||
|
||||
// test_integer_casting
|
||||
|
@ -147,7 +237,8 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
|
||||
// test_int_convert
|
||||
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
|
||||
m.def(
|
||||
|
@ -156,19 +247,27 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
return std::make_pair(input.second, input.first);
|
||||
},
|
||||
"Return a pair in reversed order");
|
||||
m.def("tuple_passthrough", [](std::tuple<bool, std::string, int> input) {
|
||||
m.def(
|
||||
"tuple_passthrough",
|
||||
[](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");
|
||||
},
|
||||
"Return a triple in reversed order");
|
||||
m.def("empty_tuple", []() { return std::tuple<>(); });
|
||||
static std::pair<RValueCaster, RValueCaster> lvpair;
|
||||
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("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("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; });
|
||||
|
||||
static std::pair<int, std::string> int_string_pair{2, "items"};
|
||||
|
@ -198,7 +297,8 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
|
||||
// test_bool_caster
|
||||
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
|
||||
#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
|
||||
// the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when
|
||||
// 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
|
||||
|
||||
// test_reference_wrapper
|
||||
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_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> {
|
||||
static UserType x(1);
|
||||
|
@ -227,15 +329,18 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
// triggers static_assert failure.
|
||||
// m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
|
||||
|
||||
m.def("refwrap_list", [](bool copy) {
|
||||
m.def(
|
||||
"refwrap_list",
|
||||
[](bool copy) {
|
||||
static IncType x1(1), x2(2);
|
||||
py::list l;
|
||||
for (auto &f : {std::ref(x1), std::ref(x2)}) {
|
||||
l.append(py::cast(f, copy ? py::return_value_policy::copy
|
||||
: py::return_value_policy::reference));
|
||||
for (const auto &f : {std::ref(x1), std::ref(x2)}) {
|
||||
l.append(py::cast(
|
||||
f, copy ? py::return_value_policy::copy : py::return_value_policy::reference));
|
||||
}
|
||||
return l;
|
||||
}, "copy"_a);
|
||||
},
|
||||
"copy"_a);
|
||||
|
||||
m.def("refwrap_iiw", [](const IncType &w) { return w.value(); });
|
||||
m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) {
|
||||
|
@ -252,7 +357,8 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
|
||||
// test_complex
|
||||
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)
|
||||
m.def("int_cast", []() { return (int) 42; });
|
||||
|
@ -274,5 +380,6 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
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_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; });
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
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 UserType, IncType
|
||||
|
||||
|
||||
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():
|
||||
"""Issue #929 - out-of-range integer values shouldn't be accepted"""
|
||||
|
@ -300,7 +310,8 @@ def test_int_convert():
|
|||
assert noconvert(7) == 7
|
||||
cant_convert(3.14159)
|
||||
# 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():
|
||||
assert convert(Int()) == 42
|
||||
else:
|
||||
|
@ -335,7 +346,9 @@ def test_numpy_int_convert():
|
|||
|
||||
# The implicit conversion from np.float32 is undesirable but currently accepted.
|
||||
# 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():
|
||||
assert convert(np.float32(3.14159)) == 3
|
||||
else:
|
||||
|
|
|
@ -40,8 +40,7 @@ TEST_SUBMODULE(call_policies, m) {
|
|||
Child(Child &&) = default;
|
||||
~Child() { py::print("Releasing child."); }
|
||||
};
|
||||
py::class_<Child>(m, "Child")
|
||||
.def(py::init<>());
|
||||
py::class_<Child>(m, "Child").def(py::init<>());
|
||||
|
||||
class Parent {
|
||||
public:
|
||||
|
@ -62,11 +61,12 @@ TEST_SUBMODULE(call_policies, m) {
|
|||
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
|
||||
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
|
||||
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>())
|
||||
.def_static(
|
||||
"staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
|
||||
.def_static("staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
|
||||
|
||||
m.def("free_function", [](Parent*, Child*) {}, py::keep_alive<1, 2>());
|
||||
m.def("invalid_arg_index", []{}, py::keep_alive<0, 1>());
|
||||
m.def(
|
||||
"free_function", [](Parent *, Child *) {}, py::keep_alive<1, 2>());
|
||||
m.def(
|
||||
"invalid_arg_index", [] {}, py::keep_alive<0, 1>());
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
// test_alive_gc
|
||||
|
@ -74,29 +74,37 @@ TEST_SUBMODULE(call_policies, m) {
|
|||
public:
|
||||
using Parent::Parent;
|
||||
};
|
||||
py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr())
|
||||
.def(py::init<>());
|
||||
py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()).def(py::init<>());
|
||||
#endif
|
||||
|
||||
// test_call_guard
|
||||
m.def("unguarded_call", &CustomGuard::report_status);
|
||||
m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>());
|
||||
|
||||
m.def("multiple_guards_correct_order", []() {
|
||||
return CustomGuard::report_status() + std::string(" & ") + DependentGuard::report_status();
|
||||
}, py::call_guard<CustomGuard, DependentGuard>());
|
||||
m.def(
|
||||
"multiple_guards_correct_order",
|
||||
[]() {
|
||||
return CustomGuard::report_status() + std::string(" & ")
|
||||
+ DependentGuard::report_status();
|
||||
},
|
||||
py::call_guard<CustomGuard, DependentGuard>());
|
||||
|
||||
m.def("multiple_guards_wrong_order", []() {
|
||||
return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status();
|
||||
}, py::call_guard<DependentGuard, CustomGuard>());
|
||||
m.def(
|
||||
"multiple_guards_wrong_order",
|
||||
[]() {
|
||||
return DependentGuard::report_status() + std::string(" & ")
|
||||
+ CustomGuard::report_status();
|
||||
},
|
||||
py::call_guard<DependentGuard, CustomGuard>());
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
|
||||
// `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
|
||||
// but it's unclear how to test it without `PyGILState_GetThisThreadState`.
|
||||
auto report_gil_status = []() {
|
||||
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());
|
||||
}
|
||||
|
||||
return is_gil_held ? "GIL held" : "GIL released";
|
||||
};
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
|
||||
from pybind11_tests import call_policies as m
|
||||
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)
|
||||
|
|
|
@ -7,11 +7,12 @@
|
|||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.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; }
|
||||
|
||||
|
@ -20,11 +21,12 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
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_callback3", [](const std::function<int(int)> &func) {
|
||||
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"));
|
||||
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")); });
|
||||
|
||||
// test_keyword_args_and_generalized_unpacking
|
||||
m.def("test_tuple_unpacking", [](const py::function &f) {
|
||||
|
@ -51,8 +53,16 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
m.def("test_unpacking_and_keywords2", [](const py::function &f) {
|
||||
auto kwargs1 = py::dict("a"_a = 1);
|
||||
auto kwargs2 = py::dict("c"_a = 3, "d"_a = 4);
|
||||
return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5,
|
||||
"key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5);
|
||||
return f("positional",
|
||||
*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) {
|
||||
|
@ -81,30 +91,74 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
};
|
||||
// Export the payload constructor statistics for testing purposes:
|
||||
m.def("payload_cstats", &ConstructorStats::get<Payload>);
|
||||
/* Test cleanup of lambda closure */
|
||||
m.def("test_cleanup", []() -> std::function<void()> {
|
||||
m.def("test_lambda_closure_cleanup", []() -> std::function<void()> {
|
||||
Payload p;
|
||||
|
||||
// In this situation, `Func` in the implementation of
|
||||
// `cpp_function::initialize` is NOT trivially destructible.
|
||||
return [p]() {
|
||||
/* p should be cleaned up when the returned function is garbage collected */
|
||||
(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 if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
|
||||
m.def("dummy_function", &dummy_function);
|
||||
m.def("dummy_function_overloaded", [](int i, int j) { return i + j; });
|
||||
m.def("dummy_function_overloaded", &dummy_function);
|
||||
m.def("dummy_function2", [](int i, int j) { return i + j; });
|
||||
m.def("roundtrip", [](std::function<int(int)> f, bool expect_none = false) {
|
||||
if (expect_none && f)
|
||||
m.def(
|
||||
"roundtrip",
|
||||
[](std::function<int(int)> f, bool expect_none = false) {
|
||||
if (expect_none && f) {
|
||||
throw std::runtime_error("Expected None to be converted to empty std::function");
|
||||
}
|
||||
return f;
|
||||
}, py::arg("f"), py::arg("expect_none")=false);
|
||||
},
|
||||
py::arg("f"),
|
||||
py::arg("expect_none") = false);
|
||||
m.def("test_dummy_function", [](const std::function<int(int)> &f) -> std::string {
|
||||
using fn_type = int (*)(int);
|
||||
auto result = f.target<fn_type>();
|
||||
const auto *result = f.target<fn_type>();
|
||||
if (!result) {
|
||||
auto r = f(1);
|
||||
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 "argument does NOT match dummy_function. This should never happen!";
|
||||
|
||||
});
|
||||
|
||||
class AbstractBase {
|
||||
|
@ -158,7 +211,8 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
|
||||
// This checks that builtin functions can be passed as callbacks
|
||||
// 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",
|
||||
[](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) {
|
||||
return sum_builtin(i);
|
||||
});
|
||||
|
||||
|
@ -176,8 +230,9 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
};
|
||||
|
||||
// spawn worker threads
|
||||
for (auto i : work)
|
||||
for (auto i : work) {
|
||||
start_f(py::cast<int>(i));
|
||||
}
|
||||
});
|
||||
|
||||
m.def("callback_num_times", [](const py::function &f, std::size_t num) {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
from pybind11_tests import callbacks as m
|
||||
from threading import Thread
|
||||
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():
|
||||
|
@ -77,13 +79,18 @@ def test_keyword_args_and_generalized_unpacking():
|
|||
|
||||
|
||||
def test_lambda_closure_cleanup():
|
||||
m.test_cleanup()
|
||||
m.test_lambda_closure_cleanup()
|
||||
cstats = m.payload_cstats()
|
||||
assert cstats.alive() == 0
|
||||
assert cstats.copy_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():
|
||||
"""Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer"""
|
||||
|
||||
|
|
|
@ -8,21 +8,20 @@
|
|||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/chrono.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
struct different_resolutions {
|
||||
using time_point_h = std::chrono::time_point<
|
||||
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_s = std::chrono::time_point<
|
||||
std::chrono::system_clock, std::chrono::seconds>;
|
||||
using time_point_ms = std::chrono::time_point<
|
||||
std::chrono::system_clock, std::chrono::milliseconds>;
|
||||
using time_point_us = std::chrono::time_point<
|
||||
std::chrono::system_clock, std::chrono::microseconds>;
|
||||
using time_point_h = std::chrono::time_point<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_s = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
|
||||
using time_point_ms
|
||||
= std::chrono::time_point<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_m timestamp_m;
|
||||
time_point_s timestamp_s;
|
||||
|
@ -65,12 +64,11 @@ TEST_SUBMODULE(chrono, m) {
|
|||
// Roundtrip a duration in microseconds from a float argument
|
||||
m.def("test_chrono7", [](std::chrono::microseconds t) { return t; });
|
||||
// Float durations (issue #719)
|
||||
m.def("test_chrono_float_diff", [](std::chrono::duration<float> a, std::chrono::duration<float> b) {
|
||||
return a - b; });
|
||||
m.def("test_chrono_float_diff",
|
||||
[](std::chrono::duration<float> a, std::chrono::duration<float> b) { return a - b; });
|
||||
|
||||
m.def("test_nano_timepoint", [](timestamp start, timespan delta) -> timestamp {
|
||||
return start + delta;
|
||||
});
|
||||
m.def("test_nano_timepoint",
|
||||
[](timestamp start, timespan delta) -> timestamp { return start + delta; });
|
||||
|
||||
// Test 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_s", &different_resolutions::timestamp_s)
|
||||
.def_readwrite("timestamp_ms", &different_resolutions::timestamp_ms)
|
||||
.def_readwrite("timestamp_us", &different_resolutions::timestamp_us)
|
||||
;
|
||||
.def_readwrite("timestamp_us", &different_resolutions::timestamp_us);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from pybind11_tests import chrono as m
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import chrono as m
|
||||
|
||||
|
||||
def test_chrono_system_clock():
|
||||
|
|
|
@ -14,20 +14,22 @@
|
|||
# include <aligned_new>
|
||||
#endif
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "local_bindings.h"
|
||||
#include <pybind11/stl.h>
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#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
|
||||
|
||||
// test_brace_initialization
|
||||
struct NoBraceInitialization {
|
||||
NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
|
||||
explicit NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
|
||||
template <typename T>
|
||||
NoBraceInitialization(std::initializer_list<T> l) : vec(l) {}
|
||||
|
||||
|
@ -47,10 +49,26 @@ TEST_SUBMODULE(class_, m) {
|
|||
}
|
||||
~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")
|
||||
.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
|
||||
class Pet {
|
||||
public:
|
||||
|
@ -58,6 +76,7 @@ TEST_SUBMODULE(class_, m) {
|
|||
: m_name(name), m_species(species) {}
|
||||
std::string name() const { return m_name; }
|
||||
std::string species() const { return m_species; }
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_species;
|
||||
|
@ -65,18 +84,18 @@ TEST_SUBMODULE(class_, m) {
|
|||
|
||||
class Dog : public Pet {
|
||||
public:
|
||||
Dog(const std::string &name) : Pet(name, "dog") {}
|
||||
explicit Dog(const std::string &name) : Pet(name, "dog") {}
|
||||
std::string bark() const { return "Woof!"; }
|
||||
};
|
||||
|
||||
class Rabbit : public Pet {
|
||||
public:
|
||||
Rabbit(const std::string &name) : Pet(name, "parrot") {}
|
||||
explicit Rabbit(const std::string &name) : Pet(name, "parrot") {}
|
||||
};
|
||||
|
||||
class Hamster : public Pet {
|
||||
public:
|
||||
Hamster(const std::string &name) : Pet(name, "rodent") {}
|
||||
explicit Hamster(const std::string &name) : Pet(name, "rodent") {}
|
||||
};
|
||||
|
||||
class Chimera : public Pet {
|
||||
|
@ -84,27 +103,24 @@ TEST_SUBMODULE(class_, m) {
|
|||
};
|
||||
|
||||
py::class_<Pet> pet_class(m, "Pet");
|
||||
pet_class
|
||||
.def(py::init<std::string, std::string>())
|
||||
pet_class.def(py::init<std::string, std::string>())
|
||||
.def("name", &Pet::name)
|
||||
.def("species", &Pet::species);
|
||||
|
||||
/* One way of declaring a subclass relationship: reference parent's class_ object */
|
||||
py::class_<Dog>(m, "Dog", pet_class)
|
||||
.def(py::init<std::string>());
|
||||
py::class_<Dog>(m, "Dog", pet_class).def(py::init<std::string>());
|
||||
|
||||
/* Another way of declaring a subclass relationship: reference parent's C++ type */
|
||||
py::class_<Rabbit, Pet>(m, "Rabbit")
|
||||
.def(py::init<std::string>());
|
||||
py::class_<Rabbit, Pet>(m, "Rabbit").def(py::init<std::string>());
|
||||
|
||||
/* And another: list parent in class template arguments */
|
||||
py::class_<Hamster, Pet>(m, "Hamster")
|
||||
.def(py::init<std::string>());
|
||||
py::class_<Hamster, Pet>(m, "Hamster").def(py::init<std::string>());
|
||||
|
||||
/* Constructors are not inherited by default */
|
||||
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(); });
|
||||
|
||||
// test_automatic_upcasting
|
||||
|
@ -124,23 +140,25 @@ TEST_SUBMODULE(class_, m) {
|
|||
m.def("return_class_1", []() -> BaseClass * { return new DerivedClass1(); });
|
||||
m.def("return_class_2", []() -> BaseClass * { return new DerivedClass2(); });
|
||||
m.def("return_class_n", [](int n) -> BaseClass * {
|
||||
if (n == 1) return new DerivedClass1();
|
||||
if (n == 2) return new DerivedClass2();
|
||||
if (n == 1) {
|
||||
return new DerivedClass1();
|
||||
}
|
||||
if (n == 2) {
|
||||
return new DerivedClass2();
|
||||
}
|
||||
return new BaseClass();
|
||||
});
|
||||
m.def("return_none", []() -> BaseClass * { return nullptr; });
|
||||
|
||||
// test_isinstance
|
||||
m.def("check_instances", [](const py::list &l) {
|
||||
return py::make_tuple(
|
||||
py::isinstance<py::tuple>(l[0]),
|
||||
return py::make_tuple(py::isinstance<py::tuple>(l[0]),
|
||||
py::isinstance<py::dict>(l[1]),
|
||||
py::isinstance<Pet>(l[2]),
|
||||
py::isinstance<Pet>(l[3]),
|
||||
py::isinstance<Dog>(l[4]),
|
||||
py::isinstance<Rabbit>(l[5]),
|
||||
py::isinstance<UnregisteredType>(l[6])
|
||||
);
|
||||
py::isinstance<UnregisteredType>(l[6]));
|
||||
});
|
||||
|
||||
struct Invalid {};
|
||||
|
@ -151,16 +169,15 @@ TEST_SUBMODULE(class_, m) {
|
|||
// See https://github.com/pybind/pybind11/issues/2486
|
||||
// if (category == 2)
|
||||
// return py::type::of<int>();
|
||||
if (category == 1)
|
||||
if (category == 1) {
|
||||
return py::type::of<DerivedClass1>();
|
||||
}
|
||||
return py::type::of<Invalid>();
|
||||
});
|
||||
|
||||
m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); });
|
||||
|
||||
m.def("get_type_classic", [](py::handle h) {
|
||||
return h.get_type();
|
||||
});
|
||||
m.def("get_type_classic", [](py::handle h) { return h.get_type(); });
|
||||
|
||||
m.def("as_type", [](const py::object &ob) { return py::type(ob); });
|
||||
|
||||
|
@ -179,16 +196,14 @@ TEST_SUBMODULE(class_, m) {
|
|||
m.def("mismatched_holder_2", []() {
|
||||
auto mod = py::module_::import("__main__");
|
||||
py::class_<MismatchBase2>(mod, "MismatchBase2");
|
||||
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>,
|
||||
MismatchBase2>(mod, "MismatchDerived2");
|
||||
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(
|
||||
mod, "MismatchDerived2");
|
||||
});
|
||||
|
||||
// test_override_static
|
||||
// #511: problem with inheritance + overwritten def_static
|
||||
struct MyBase {
|
||||
static std::unique_ptr<MyBase> make() {
|
||||
return std::unique_ptr<MyBase>(new MyBase());
|
||||
}
|
||||
static std::unique_ptr<MyBase> make() { return std::unique_ptr<MyBase>(new MyBase()); }
|
||||
};
|
||||
|
||||
struct MyDerived : MyBase {
|
||||
|
@ -197,8 +212,7 @@ TEST_SUBMODULE(class_, m) {
|
|||
}
|
||||
};
|
||||
|
||||
py::class_<MyBase>(m, "MyBase")
|
||||
.def_static("make", &MyBase::make);
|
||||
py::class_<MyBase>(m, "MyBase").def_static("make", &MyBase::make);
|
||||
|
||||
py::class_<MyDerived, MyBase>(m, "MyDerived")
|
||||
.def_static("make", &MyDerived::make)
|
||||
|
@ -208,11 +222,10 @@ TEST_SUBMODULE(class_, m) {
|
|||
struct ConvertibleFromUserType {
|
||||
int i;
|
||||
|
||||
ConvertibleFromUserType(UserType u) : i(u.value()) { }
|
||||
explicit ConvertibleFromUserType(UserType u) : i(u.value()) {}
|
||||
};
|
||||
|
||||
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType")
|
||||
.def(py::init<UserType>());
|
||||
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType").def(py::init<UserType>());
|
||||
py::implicitly_convertible<UserType, ConvertibleFromUserType>();
|
||||
|
||||
m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; });
|
||||
|
@ -234,49 +247,91 @@ TEST_SUBMODULE(class_, m) {
|
|||
return py::str().release().ptr();
|
||||
};
|
||||
|
||||
auto def = new PyMethodDef{"f", f, METH_VARARGS, nullptr};
|
||||
py::capsule def_capsule(def, [](void *ptr) { delete reinterpret_cast<PyMethodDef *>(ptr); });
|
||||
return py::reinterpret_steal<py::object>(PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr()));
|
||||
auto *def = new PyMethodDef{"f", f, METH_VARARGS, nullptr};
|
||||
py::capsule def_capsule(def,
|
||||
[](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
|
||||
struct HasOpNewDel {
|
||||
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, void *ptr) { py::print("A placement-new", s); return ptr; }
|
||||
static void operator delete(void *p) { py::print("A delete"); return ::operator delete(p); }
|
||||
static void *operator new(size_t s) {
|
||||
py::print("A new", s);
|
||||
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 {
|
||||
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, 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); }
|
||||
static void *operator new(size_t s) {
|
||||
py::print("B new", s);
|
||||
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 {
|
||||
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, 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); }
|
||||
static void *operator new(size_t s) {
|
||||
py::print("C new", s);
|
||||
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;
|
||||
AliasedHasOpNewDelSize() = default;
|
||||
AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize &) = delete;
|
||||
};
|
||||
struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize {
|
||||
PyAliasedHasOpNewDelSize() = default;
|
||||
PyAliasedHasOpNewDelSize(int) { }
|
||||
explicit PyAliasedHasOpNewDelSize(int) {}
|
||||
std::uint64_t j;
|
||||
};
|
||||
struct HasOpNewDelBoth {
|
||||
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, 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); }
|
||||
static void *operator new(size_t s) {
|
||||
py::print("D new", s);
|
||||
return ::operator new(s);
|
||||
}
|
||||
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_<HasOpNewDelSize>(m, "HasOpNewDelSize").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.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize));
|
||||
aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize));
|
||||
|
@ -380,8 +435,8 @@ TEST_SUBMODULE(class_, m) {
|
|||
py::class_<Nested>(base, "Nested")
|
||||
.def(py::init<>())
|
||||
.def("fn", [](Nested &, int, NestBase &, Nested &) {})
|
||||
.def("fa", [](Nested &, int, NestBase &, Nested &) {},
|
||||
"a"_a, "b"_a, "c"_a);
|
||||
.def(
|
||||
"fa", [](Nested &, int, NestBase &, Nested &) {}, "a"_a, "b"_a, "c"_a);
|
||||
base.def("g", [](NestBase &, Nested &) {});
|
||||
base.def("h", []() { return NestBase(); });
|
||||
|
||||
|
@ -391,7 +446,9 @@ TEST_SUBMODULE(class_, m) {
|
|||
// generate a useful error message
|
||||
|
||||
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",
|
||||
[](const StringWrapper &) -> NotRegistered { return {}; });
|
||||
|
@ -402,9 +459,7 @@ TEST_SUBMODULE(class_, m) {
|
|||
struct alignas(1024) Aligned {
|
||||
std::uintptr_t ptr() const { return (uintptr_t) this; }
|
||||
};
|
||||
py::class_<Aligned>(m, "Aligned")
|
||||
.def(py::init<>())
|
||||
.def("ptr", &Aligned::ptr);
|
||||
py::class_<Aligned>(m, "Aligned").def(py::init<>()).def("ptr", &Aligned::ptr);
|
||||
#endif
|
||||
|
||||
// test_final
|
||||
|
@ -418,9 +473,7 @@ TEST_SUBMODULE(class_, m) {
|
|||
// test_exception_rvalue_abort
|
||||
struct PyPrintDestructor {
|
||||
PyPrintDestructor() = default;
|
||||
~PyPrintDestructor() {
|
||||
py::print("Print from destructor");
|
||||
}
|
||||
~PyPrintDestructor() { py::print("Print from destructor"); }
|
||||
void throw_something() { throw std::runtime_error("error"); }
|
||||
};
|
||||
py::class_<PyPrintDestructor>(m, "PyPrintDestructor")
|
||||
|
@ -434,8 +487,7 @@ TEST_SUBMODULE(class_, m) {
|
|||
.def(py::init([]() { return &samePointer; }));
|
||||
|
||||
struct Empty {};
|
||||
py::class_<Empty>(m, "Empty")
|
||||
.def(py::init<>());
|
||||
py::class_<Empty>(m, "Empty").def(py::init<>());
|
||||
|
||||
// test_base_and_derived_nested_scope
|
||||
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;
|
||||
BreaksBase() = default;
|
||||
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:
|
||||
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>>>;
|
||||
|
@ -492,43 +547,83 @@ using DoesntBreak5 = py::class_<BreaksBase<5>>;
|
|||
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 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) \
|
||||
static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<(N)>>::value, \
|
||||
"DoesntBreak" #N " has wrong type!")
|
||||
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, \
|
||||
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!")
|
||||
#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \
|
||||
#define CHECK_NOALIAS(N) \
|
||||
static_assert(!DoesntBreak##N::has_alias \
|
||||
&& std::is_void<typename DoesntBreak##N::type_alias>::value, \
|
||||
"DoesntBreak" #N " has type alias, but shouldn't!")
|
||||
CHECK_ALIAS(1); CHECK_ALIAS(2); 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, \
|
||||
CHECK_ALIAS(1);
|
||||
CHECK_ALIAS(2);
|
||||
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);
|
||||
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,
|
||||
// though, so that they can be manually tested by uncommenting them (and seeing that compilation
|
||||
// failures occurs).
|
||||
|
||||
// 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) \
|
||||
static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-(N)>>::value, \
|
||||
"Breaks1 has wrong type!");
|
||||
|
||||
//// Two holder classes:
|
||||
//typedef py::class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>> Breaks1;
|
||||
//CHECK_BROKEN(1);
|
||||
//// Two aliases:
|
||||
//typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
|
||||
//CHECK_BROKEN(2);
|
||||
//// Holder + 2 aliases
|
||||
//typedef py::class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>> Breaks3;
|
||||
//CHECK_BROKEN(3);
|
||||
//// Alias + 2 holders
|
||||
//typedef py::class_<BreaksBase<-4>, std::unique_ptr<BreaksBase<-4>>, BreaksTramp<-4>, 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);
|
||||
#ifdef PYBIND11_NEVER_DEFINED_EVER
|
||||
// Two holder classes:
|
||||
typedef py::
|
||||
class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>>
|
||||
Breaks1;
|
||||
CHECK_BROKEN(1);
|
||||
// Two aliases:
|
||||
typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
|
||||
CHECK_BROKEN(2);
|
||||
// Holder + 2 aliases
|
||||
typedef py::
|
||||
class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>>
|
||||
Breaks3;
|
||||
CHECK_BROKEN(3);
|
||||
// Alias + 2 holders
|
||||
typedef py::class_<BreaksBase<-4>,
|
||||
std::unique_ptr<BreaksBase<-4>>,
|
||||
BreaksTramp<-4>,
|
||||
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
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
|
||||
from pybind11_tests import ConstructorStats, UserType
|
||||
from pybind11_tests import class_ as m
|
||||
from pybind11_tests import UserType, ConstructorStats
|
||||
|
||||
|
||||
def test_repr():
|
||||
|
@ -26,6 +25,14 @@ def test_instance(msg):
|
|||
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():
|
||||
assert m.check_type(1) == m.DerivedClass1
|
||||
with pytest.raises(RuntimeError) as execinfo:
|
||||
|
|
|
@ -6,15 +6,17 @@ PYBIND11_EMBEDDED_MODULE(test_cmake_build, m) {
|
|||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2)
|
||||
if (argc != 2) {
|
||||
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{};
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
py::module_::import("sys").attr("argv") = py::make_tuple("test.py", "embed.cpp");
|
||||
py::eval_file(test_py_file, py::globals());
|
||||
|
|
|
@ -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.
|
||||
set_target_properties(test_installed_embed PROPERTIES NO_SYSTEM_FROM_IMPORTED ON)
|
||||
|
||||
add_custom_target(check_installed_embed $<TARGET_FILE:test_installed_embed>
|
||||
${PROJECT_SOURCE_DIR}/../test.py)
|
||||
add_custom_target(
|
||||
check_installed_embed
|
||||
$<TARGET_FILE:test_installed_embed> ${PROJECT_SOURCE_DIR}/../test.py
|
||||
DEPENDS test_installed_embed)
|
||||
|
|
|
@ -35,4 +35,5 @@ add_custom_target(
|
|||
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_function>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME})
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_installed_function)
|
||||
|
|
|
@ -42,4 +42,5 @@ add_custom_target(
|
|||
PYTHONPATH=$<TARGET_FILE_DIR:test_installed_target>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME})
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_installed_target)
|
||||
|
|
|
@ -23,8 +23,10 @@ add_executable(test_subdirectory_embed ../embed.cpp)
|
|||
target_link_libraries(test_subdirectory_embed PRIVATE pybind11::embed)
|
||||
set_target_properties(test_subdirectory_embed PROPERTIES OUTPUT_NAME test_cmake_build)
|
||||
|
||||
add_custom_target(check_subdirectory_embed $<TARGET_FILE:test_subdirectory_embed>
|
||||
"${PROJECT_SOURCE_DIR}/../test.py")
|
||||
add_custom_target(
|
||||
check_subdirectory_embed
|
||||
$<TARGET_FILE:test_subdirectory_embed> "${PROJECT_SOURCE_DIR}/../test.py"
|
||||
DEPENDS test_subdirectory_embed)
|
||||
|
||||
# Test custom export group -- PYBIND11_EXPORT_NAME
|
||||
add_library(test_embed_lib ../embed.cpp)
|
||||
|
|
|
@ -31,4 +31,5 @@ add_custom_target(
|
|||
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_function>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME})
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_subdirectory_function)
|
||||
|
|
|
@ -37,4 +37,5 @@ add_custom_target(
|
|||
PYTHONPATH=$<TARGET_FILE_DIR:test_subdirectory_target>
|
||||
${_Python_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/../test.py
|
||||
${PROJECT_NAME})
|
||||
${PROJECT_NAME}
|
||||
DEPENDS test_subdirectory_target)
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
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
|
||||
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -12,17 +12,11 @@
|
|||
|
||||
enum MyEnum { EFirstEntry = 1, ESecondEntry };
|
||||
|
||||
std::string test_function1() {
|
||||
return "test_function()";
|
||||
}
|
||||
std::string test_function1() { return "test_function()"; }
|
||||
|
||||
std::string test_function2(MyEnum k) {
|
||||
return "test_function(enum=" + std::to_string(k) + ")";
|
||||
}
|
||||
std::string test_function2(MyEnum k) { return "test_function(enum=" + std::to_string(k) + ")"; }
|
||||
|
||||
std::string test_function3(int i) {
|
||||
return "test_function(" + std::to_string(i) + ")";
|
||||
}
|
||||
std::string test_function3(int i) { return "test_function(" + std::to_string(i) + ")"; }
|
||||
|
||||
py::str test_function4() { return "test_function()"; }
|
||||
py::str test_function4(char *) { return "test_function(char *)"; }
|
||||
|
@ -44,8 +38,9 @@ std::string print_bytes(const py::bytes &bytes) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Test that we properly handle C++17 exception specifiers (which are part of the function signature
|
||||
// in C++17). These should all still work before C++17, but don't affect the function signature.
|
||||
// Test that we properly handle C++17 exception specifiers (which are part of the function
|
||||
// signature in C++17). These should all still work before C++17, but don't affect the function
|
||||
// signature.
|
||||
namespace test_exc_sp {
|
||||
// [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
|
||||
|
@ -87,7 +82,6 @@ struct C {
|
|||
};
|
||||
} // namespace test_exc_sp
|
||||
|
||||
|
||||
TEST_SUBMODULE(constants_and_functions, m) {
|
||||
// test_constants
|
||||
m.attr("some_constant") = py::int_(14);
|
||||
|
@ -129,8 +123,7 @@ TEST_SUBMODULE(constants_and_functions, m) {
|
|||
.def("m5", &C::m5)
|
||||
.def("m6", &C::m6)
|
||||
.def("m7", &C::m7)
|
||||
.def("m8", &C::m8)
|
||||
;
|
||||
.def("m8", &C::m8);
|
||||
m.def("f1", f1);
|
||||
m.def("f2", f2);
|
||||
#if defined(__INTEL_COMPILER)
|
||||
|
@ -151,7 +144,11 @@ TEST_SUBMODULE(constants_and_functions, 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
|
||||
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(
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
template <typename derived>
|
||||
struct empty {
|
||||
static const derived &get_one() { return instance_; }
|
||||
|
@ -23,7 +24,8 @@ struct lacking_copy_ctor : public empty<lacking_copy_ctor> {
|
|||
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> {
|
||||
lacking_move_ctor() = default;
|
||||
|
@ -31,13 +33,14 @@ struct lacking_move_ctor : public empty<lacking_move_ctor> {
|
|||
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 */
|
||||
class MoveOnlyInt {
|
||||
public:
|
||||
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 {
|
||||
print_move_created(this, m.value);
|
||||
std::swap(value, m.value);
|
||||
|
@ -56,7 +59,7 @@ public:
|
|||
class MoveOrCopyInt {
|
||||
public:
|
||||
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 {
|
||||
print_move_created(this, m.value);
|
||||
std::swap(value, m.value);
|
||||
|
@ -66,8 +69,16 @@ public:
|
|||
std::swap(value, m.value);
|
||||
return *this;
|
||||
}
|
||||
MoveOrCopyInt(const MoveOrCopyInt &c) { print_copy_created(this, c.value); value = c.value; }
|
||||
MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
|
||||
MoveOrCopyInt(const MoveOrCopyInt &c) {
|
||||
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); }
|
||||
|
||||
int value;
|
||||
|
@ -75,41 +86,71 @@ public:
|
|||
class CopyOnlyInt {
|
||||
public:
|
||||
CopyOnlyInt() { print_default_created(this); }
|
||||
CopyOnlyInt(int v) : value{v} { print_created(this, value); }
|
||||
CopyOnlyInt(const CopyOnlyInt &c) { print_copy_created(this, c.value); value = c.value; }
|
||||
CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
|
||||
explicit CopyOnlyInt(int v) : value{v} { print_created(this, value); }
|
||||
CopyOnlyInt(const CopyOnlyInt &c) {
|
||||
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); }
|
||||
|
||||
int value;
|
||||
};
|
||||
PYBIND11_NAMESPACE_BEGIN(pybind11)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <> struct type_caster<MoveOnlyInt> {
|
||||
PYBIND11_TYPE_CASTER(MoveOnlyInt, _x("MoveOnlyInt"));
|
||||
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<MoveOnlyInt> {
|
||||
PYBIND11_TYPE_CASTER(MoveOnlyInt, const_name("MoveOnlyInt"));
|
||||
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> {
|
||||
PYBIND11_TYPE_CASTER(MoveOrCopyInt, _x("MoveOrCopyInt"));
|
||||
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<MoveOrCopyInt> {
|
||||
PYBIND11_TYPE_CASTER(MoveOrCopyInt, const_name("MoveOrCopyInt"));
|
||||
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:
|
||||
CopyOnlyInt value;
|
||||
|
||||
public:
|
||||
static constexpr auto name = _x("CopyOnlyInt");
|
||||
bool load(handle src, bool) { 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 constexpr auto name = const_name("CopyOnlyInt");
|
||||
bool load(handle src, bool) {
|
||||
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) {
|
||||
if (!src) return none().release();
|
||||
if (!src) {
|
||||
return none().release();
|
||||
}
|
||||
return cast(*src, policy, parent);
|
||||
}
|
||||
operator CopyOnlyInt*() { return &value; }
|
||||
operator CopyOnlyInt&() { return value; }
|
||||
template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>;
|
||||
explicit operator CopyOnlyInt *() { return &value; }
|
||||
explicit operator CopyOnlyInt &() { return value; }
|
||||
template <typename T>
|
||||
using cast_op_type = pybind11::detail::cast_op_type<T>;
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
|
@ -117,12 +158,10 @@ PYBIND11_NAMESPACE_END(pybind11)
|
|||
TEST_SUBMODULE(copy_move_policies, m) {
|
||||
// test_lacking_copy_ctor
|
||||
py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
|
||||
.def_static("get_one", &lacking_copy_ctor::get_one,
|
||||
py::return_value_policy::copy);
|
||||
.def_static("get_one", &lacking_copy_ctor::get_one, py::return_value_policy::copy);
|
||||
// test_lacking_move_ctor
|
||||
py::class_<lacking_move_ctor>(m, "lacking_move_ctor")
|
||||
.def_static("get_one", &lacking_move_ctor::get_one,
|
||||
py::return_value_policy::move);
|
||||
.def_static("get_one", &lacking_move_ctor::get_one, py::return_value_policy::move);
|
||||
|
||||
// test_move_and_copy_casts
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
|
@ -147,28 +186,34 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
|||
// Changing this breaks the existing test: needs careful review.
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
|
||||
m.def("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) {
|
||||
return p.first.value + p.second.value;
|
||||
});
|
||||
m.def("move_pair",
|
||||
[](std::pair<MoveOnlyInt, MoveOrCopyInt> p) { return p.first.value + p.second.value; });
|
||||
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;
|
||||
});
|
||||
m.def("copy_tuple", [](std::tuple<CopyOnlyInt, CopyOnlyInt> t) {
|
||||
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) {
|
||||
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_copy_nested",
|
||||
[](std::pair<MoveOnlyInt,
|
||||
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", []() {
|
||||
ConstructorStats::gc();
|
||||
// Reset counts to 0 so that previous tests don't affect later ones:
|
||||
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>();
|
||||
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>();
|
||||
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;
|
||||
d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference);
|
||||
d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference);
|
||||
|
@ -178,16 +223,11 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
|||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
// test_move_and_copy_load_optional
|
||||
m.attr("has_optional") = true;
|
||||
m.def("move_optional", [](std::optional<MoveOnlyInt> o) {
|
||||
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_optional_tuple", [](std::optional<std::tuple<MoveOrCopyInt, MoveOnlyInt, CopyOnlyInt>> x) {
|
||||
m.def("move_optional", [](std::optional<MoveOnlyInt> o) { 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_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
|
||||
|
@ -200,39 +240,53 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
|||
// added later.
|
||||
struct PrivateOpNew {
|
||||
int value = 1;
|
||||
|
||||
private:
|
||||
void *operator new(size_t bytes) {
|
||||
void *ptr = std::malloc(bytes);
|
||||
if (ptr)
|
||||
if (ptr) {
|
||||
return ptr;
|
||||
}
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
};
|
||||
py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
|
||||
m.def("private_op_new_value", []() { return PrivateOpNew(); });
|
||||
m.def("private_op_new_reference", []() -> const PrivateOpNew & {
|
||||
m.def(
|
||||
"private_op_new_reference",
|
||||
[]() -> const PrivateOpNew & {
|
||||
static PrivateOpNew x{};
|
||||
return x;
|
||||
}, py::return_value_policy::reference);
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// test_move_fallback
|
||||
// #389: rvp::move should fall-through to copy on non-movable objects
|
||||
struct MoveIssue1 {
|
||||
int v;
|
||||
MoveIssue1(int v) : v{v} {}
|
||||
explicit MoveIssue1(int v) : v{v} {}
|
||||
MoveIssue1(const MoveIssue1 &c) = default;
|
||||
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 {
|
||||
int v;
|
||||
MoveIssue2(int v) : v{v} {}
|
||||
explicit MoveIssue2(int v) : v{v} {}
|
||||
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`
|
||||
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);
|
||||
// #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with
|
||||
// `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);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import copy_move_policies as m
|
||||
|
||||
|
||||
|
|
|
@ -7,23 +7,37 @@
|
|||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
// py::arg/py::arg_v testing: these arguments just record their argument when invoked
|
||||
class ArgInspector1 { public: std::string arg = "(default arg inspector 1)"; };
|
||||
class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; };
|
||||
class ArgAlwaysConverts { };
|
||||
namespace pybind11 { namespace detail {
|
||||
template <> struct type_caster<ArgInspector1> {
|
||||
class ArgInspector1 {
|
||||
public:
|
||||
PYBIND11_TYPE_CASTER(ArgInspector1, _x("ArgInspector1"));
|
||||
std::string arg = "(default arg inspector 1)";
|
||||
};
|
||||
class ArgInspector2 {
|
||||
public:
|
||||
std::string arg = "(default arg inspector 2)";
|
||||
};
|
||||
class ArgAlwaysConverts {};
|
||||
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<ArgInspector1> {
|
||||
public:
|
||||
// Classic
|
||||
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1"));
|
||||
#else
|
||||
PYBIND11_TYPE_CASTER(ArgInspector1, const_name("ArgInspector1"));
|
||||
#endif
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
value.arg = "loading ArgInspector1 argument " +
|
||||
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
|
||||
"Argument value = " + (std::string) str(src);
|
||||
value.arg = "loading ArgInspector1 argument " + std::string(convert ? "WITH" : "WITHOUT")
|
||||
+ " conversion allowed. "
|
||||
"Argument value = "
|
||||
+ (std::string) str(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -31,14 +45,16 @@ public:
|
|||
return str(src.arg).release();
|
||||
}
|
||||
};
|
||||
template <> struct type_caster<ArgInspector2> {
|
||||
template <>
|
||||
struct type_caster<ArgInspector2> {
|
||||
public:
|
||||
PYBIND11_TYPE_CASTER(ArgInspector2, _x("ArgInspector2"));
|
||||
PYBIND11_TYPE_CASTER(ArgInspector2, const_name("ArgInspector2"));
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
value.arg = "loading ArgInspector2 argument " +
|
||||
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
|
||||
"Argument value = " + (std::string) str(src);
|
||||
value.arg = "loading ArgInspector2 argument " + std::string(convert ? "WITH" : "WITHOUT")
|
||||
+ " conversion allowed. "
|
||||
"Argument value = "
|
||||
+ (std::string) str(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -46,13 +62,12 @@ public:
|
|||
return str(src.arg).release();
|
||||
}
|
||||
};
|
||||
template <> struct type_caster<ArgAlwaysConverts> {
|
||||
template <>
|
||||
struct type_caster<ArgAlwaysConverts> {
|
||||
public:
|
||||
PYBIND11_TYPE_CASTER(ArgAlwaysConverts, _x("ArgAlwaysConverts"));
|
||||
PYBIND11_TYPE_CASTER(ArgAlwaysConverts, const_name("ArgAlwaysConverts"));
|
||||
|
||||
bool load(handle, bool convert) {
|
||||
return convert;
|
||||
}
|
||||
bool load(handle, bool convert) { return convert; }
|
||||
|
||||
static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) {
|
||||
return py::none().release();
|
||||
|
@ -68,15 +83,20 @@ public:
|
|||
~DestructionTester() { print_destroyed(this); }
|
||||
DestructionTester(const DestructionTester &) { print_copy_created(this); }
|
||||
DestructionTester(DestructionTester &&) noexcept { print_move_created(this); }
|
||||
DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; }
|
||||
DestructionTester &operator=(const DestructionTester &) {
|
||||
print_copy_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
DestructionTester &operator=(DestructionTester &&) noexcept {
|
||||
print_move_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
namespace pybind11 { namespace detail {
|
||||
template <> struct type_caster<DestructionTester> {
|
||||
PYBIND11_TYPE_CASTER(DestructionTester, _x("DestructionTester"));
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<DestructionTester> {
|
||||
PYBIND11_TYPE_CASTER(DestructionTester, const_name("DestructionTester"));
|
||||
bool load(handle, bool) { return true; }
|
||||
|
||||
static handle cast(const DestructionTester &, return_value_policy, handle) {
|
||||
|
@ -86,6 +106,34 @@ template <> struct type_caster<DestructionTester> {
|
|||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
|
||||
// Define type caster outside of `pybind11::detail` and then alias it.
|
||||
namespace other_lib {
|
||||
struct MyType {};
|
||||
// Corrupt `py` shorthand alias for surrounding context.
|
||||
namespace py {}
|
||||
// Corrupt unqualified relative `pybind11` namespace.
|
||||
namespace pybind11 {}
|
||||
// Correct alias.
|
||||
namespace py_ = ::pybind11;
|
||||
// Define caster. This is effectively no-op, we only ensure it compiles and we
|
||||
// don't have any symbol collision when using macro mixin.
|
||||
struct my_caster {
|
||||
PYBIND11_TYPE_CASTER(MyType, py_::detail::const_name("MyType"));
|
||||
bool load(py_::handle, bool) { return true; }
|
||||
|
||||
static py_::handle cast(const MyType &, py_::return_value_policy, py_::handle) {
|
||||
return py_::bool_(true).release();
|
||||
}
|
||||
};
|
||||
} // namespace other_lib
|
||||
// Effectively "alias" it into correct namespace (via inheritance).
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<other_lib::MyType> : public other_lib::my_caster {};
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
|
||||
TEST_SUBMODULE(custom_type_casters, m) {
|
||||
// test_custom_type_casters
|
||||
|
||||
|
@ -110,9 +158,14 @@ TEST_SUBMODULE(custom_type_casters, m) {
|
|||
py::class_<ArgInspector>(m, "ArgInspector")
|
||||
.def(py::init<>())
|
||||
.def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts())
|
||||
.def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts())
|
||||
.def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts())
|
||||
;
|
||||
.def("g",
|
||||
&ArgInspector::g,
|
||||
"a"_a.noconvert(),
|
||||
"b"_a,
|
||||
"c"_a.noconvert() = 13,
|
||||
"d"_a = ArgInspector2(),
|
||||
py::arg() = ArgAlwaysConverts())
|
||||
.def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts());
|
||||
m.def(
|
||||
"arg_inspect_func",
|
||||
[](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) {
|
||||
|
@ -122,20 +175,35 @@ TEST_SUBMODULE(custom_type_casters, m) {
|
|||
py::arg_v(nullptr, ArgInspector1()).noconvert(true),
|
||||
py::arg() = ArgAlwaysConverts());
|
||||
|
||||
m.def("floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
|
||||
m.def("floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());
|
||||
m.def("ints_preferred", [](int i) { return i / 2; }, "i"_a);
|
||||
m.def("ints_only", [](int i) { return i / 2; }, "i"_a.noconvert());
|
||||
m.def(
|
||||
"floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
|
||||
m.def(
|
||||
"floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());
|
||||
m.def(
|
||||
"ints_preferred", [](int i) { return i / 2; }, "i"_a);
|
||||
m.def(
|
||||
"ints_only", [](int i) { return i / 2; }, "i"_a.noconvert());
|
||||
|
||||
// test_custom_caster_destruction
|
||||
// Test that `take_ownership` works on types with a custom type caster when given a pointer
|
||||
|
||||
// default policy: don't take ownership:
|
||||
m.def("custom_caster_no_destroy", []() { static auto *dt = new DestructionTester(); return dt; });
|
||||
m.def("custom_caster_no_destroy", []() {
|
||||
static auto *dt = new DestructionTester();
|
||||
return dt;
|
||||
});
|
||||
|
||||
m.def("custom_caster_destroy", []() { return new DestructionTester(); },
|
||||
m.def(
|
||||
"custom_caster_destroy",
|
||||
[]() { return new DestructionTester(); },
|
||||
py::return_value_policy::take_ownership); // Takes ownership: destroy when finished
|
||||
m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); },
|
||||
m.def(
|
||||
"custom_caster_destroy_const",
|
||||
[]() -> const DestructionTester * { return new DestructionTester(); },
|
||||
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
|
||||
m.def("destruction_tester_cstats", &ConstructorStats::get<DestructionTester>, py::return_value_policy::reference);
|
||||
m.def("destruction_tester_cstats",
|
||||
&ConstructorStats::get<DestructionTester>,
|
||||
py::return_value_policy::reference);
|
||||
|
||||
m.def("other_lib_type", [](other_lib::MyType x) { return x; });
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue