Merge branch 'smart_holder' into native_enum_internals_sh

This commit is contained in:
Ralf W. Grosse-Kunstleve 2024-08-25 13:42:55 -07:00
commit b77283dacf
69 changed files with 1246 additions and 2198 deletions

View File

@ -81,7 +81,7 @@ nox -s build
### Full setup ### Full setup
To setup an ideal development environment, run the following commands on a To setup an ideal development environment, run the following commands on a
system with CMake 3.14+: system with CMake 3.15+:
```bash ```bash
python3 -m venv venv python3 -m venv venv
@ -96,8 +96,8 @@ Tips:
* You can use `virtualenv` (faster, from PyPI) instead of `venv`. * You can use `virtualenv` (faster, from PyPI) instead of `venv`.
* You can select any name for your environment folder; if it contains "env" it * You can select any name for your environment folder; if it contains "env" it
will be ignored by git. will be ignored by git.
* If you don't have CMake 3.14+, just add "cmake" to the pip install command. * If you don't have CMake 3.15+, just add "cmake" to the pip install command.
* You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+ * You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython.
* In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`. * In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`.
FindPython uses `-DPython_ROOT_DIR=/path/to` or FindPython uses `-DPython_ROOT_DIR=/path/to` or
`-DPython_EXECUTABLE=/path/to/python`. `-DPython_EXECUTABLE=/path/to/python`.
@ -149,8 +149,8 @@ To run the tests, you can "build" the check target:
cmake --build build --target check cmake --build build --target check
``` ```
`--target` can be spelled `-t` in CMake 3.15+. You can also run individual `--target` can be spelled `-t`. You can also run individual tests with these
tests with these targets: targets:
* `pytest`: Python tests only, using the * `pytest`: Python tests only, using the
[pytest](https://docs.pytest.org/en/stable/) framework [pytest](https://docs.pytest.org/en/stable/) framework

View File

@ -68,7 +68,18 @@ jobs:
# Extra ubuntu latest job # Extra ubuntu latest job
- runs-on: ubuntu-latest - runs-on: ubuntu-latest
python: '3.11' python: '3.11'
- runs-on: ubuntu-latest
python: '3.12'
args: >
-DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
- runs-on: macos-13
python: '3.12'
args: >
-DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
- runs-on: windows-2022
python: '3.12'
args: >
-DCMAKE_CXX_FLAGS="/DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT /GR /EHsc"
name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}"
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}

File diff suppressed because it is too large Load Diff

View File

@ -1,223 +0,0 @@
--- ci.yml 2024-07-30 11:20:28.997003056 -0700
+++ ci_sh_def.yml 2024-07-30 11:21:39.724969167 -0700
@@ -1,4 +1,16 @@
-name: CI
+# PLEASE KEEP THIS GROUP OF FILES IN SYNC AT ALL TIMES:
+# ci.yml
+# ci_sh_def.yml
+# ci_sh_def.yml.patch
+# To import changes made to ci.yml *** ESPECIALLY AFTER `git merge master` ***:
+# patch -i ci_sh_def.yml.patch -o ci_sh_def.yml
+# To update the patch file after making changes to ci_sh.yml:
+# diff -u ci.yml ci_sh_def.yml > ci_sh_def.yml.patch
+# git commit -a -m 'Tracking ci.yml changes from master.'
+#
+# Thanks a lot to @rhaschke for PR #2930!
+
+name: "CI-SH-DEF"
on:
workflow_dispatch:
@@ -13,7 +25,7 @@
permissions: read-all
concurrency:
- group: test-sh-avl${{ github.ref }}
+ group: test-sh-def-${{ github.ref }}
cancel-in-progress: true
env:
@@ -126,6 +138,7 @@
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=11
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT ${{runner.os == 'Windows' && '/GR /EHsc' || ''}}"
${{ matrix.args }}
- name: Build C++11
@@ -155,6 +168,7 @@
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=17
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT ${{runner.os == 'Windows' && '/GR /EHsc' || ''}}"
${{ matrix.args }}
- name: Build
@@ -174,6 +188,7 @@
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=17
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT ${{runner.os == 'Windows' && '/GR /EHsc' || ''}}"
-DPYBIND11_INTERNALS_VERSION=10000000
${{ matrix.args }}
@@ -217,6 +232,7 @@
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DPython_ROOT_DIR=.venv
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
- name: Build C++11
run: cmake --build build -j2
@@ -290,6 +306,7 @@
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=17
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
- name: Build
run: cmake --build build -j 2
@@ -357,6 +374,7 @@
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DCMAKE_CXX_STANDARD=${{ matrix.std }}
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
- name: Build
@@ -386,7 +404,7 @@
run: apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y cmake git python3-dev python3-pytest python3-numpy
- name: Configure
- run: cmake -S . -B build -DPYBIND11_CUDA_TESTS=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
+ run: cmake -S . -B build -DPYBIND11_CUDA_TESTS=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
- name: Build
run: cmake --build build -j2 --verbose
@@ -474,7 +492,7 @@
cmake -S . -B build -DDOWNLOAD_CATCH=ON \
-DCMAKE_CXX_STANDARD=17 \
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \
- -DCMAKE_CXX_FLAGS="-Wc,--pending_instantiations=0" \
+ -DCMAKE_CXX_FLAGS="-Wc,--pending_instantiations=0 -DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" \
-DPYBIND11_TEST_FILTER="test_smart_ptr.cpp"
- name: Build
@@ -526,6 +544,7 @@
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DCMAKE_CXX_STANDARD=${{ matrix.std }}
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
- name: Build
@@ -548,6 +567,7 @@
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DCMAKE_CXX_STANDARD=${{ matrix.std }}
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
@@ -597,6 +617,7 @@
-DDOWNLOAD_CATCH=ON \
-DDOWNLOAD_EIGEN=OFF \
-DCMAKE_CXX_STANDARD=11 \
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" \
-DCMAKE_CXX_COMPILER=$(which icpc) \
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
@@ -629,6 +650,7 @@
-DDOWNLOAD_CATCH=ON \
-DDOWNLOAD_EIGEN=OFF \
-DCMAKE_CXX_STANDARD=17 \
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT" \
-DCMAKE_CXX_COMPILER=$(which icpc) \
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
@@ -700,6 +722,7 @@
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=11
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
- name: Build
@@ -750,6 +773,7 @@
cmake ../pybind11-tests
-DDOWNLOAD_CATCH=ON
-DPYBIND11_WERROR=ON
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
working-directory: /build-tests
@@ -850,6 +874,7 @@
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
+ -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
${{ matrix.args }}
- name: Build C++11
run: cmake --build build -j 2
@@ -904,6 +929,7 @@
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
+ -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
${{ matrix.args }}
- name: Build C++11
run: cmake --build build --config Debug -j 2
@@ -946,6 +972,7 @@
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=20
+ -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
- name: Build C++20
run: cmake --build build -j 2
@@ -966,6 +993,7 @@
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=20
+ -DCMAKE_CXX_FLAGS="/GR /EHsc /DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"
- name: Build C++20 - Exercise cmake -DPYBIND11_TEST_OVERRIDE
@@ -1018,6 +1046,7 @@
run: >-
cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
-DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)")
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-S . -B build
- name: Build C++11
@@ -1039,6 +1068,7 @@
run: >-
cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
-DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)")
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-S . -B build2
- name: Build C++14
@@ -1060,6 +1090,7 @@
run: >-
cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
-DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)")
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-S . -B build3
- name: Build C++17
@@ -1127,6 +1158,7 @@
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_CXX_STANDARD=17
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
- name: Build
run: cmake --build . -j 2
@@ -1192,6 +1224,7 @@
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_CXX_STANDARD=17
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
- name: Build
@@ -1215,6 +1248,7 @@
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_CXX_STANDARD=17
+ -DCMAKE_CXX_FLAGS="-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT"
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)")
"-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp"

View File

@ -32,7 +32,7 @@ jobs:
include: include:
- runs-on: ubuntu-20.04 - runs-on: ubuntu-20.04
arch: x64 arch: x64
cmake: "3.5" cmake: "3.15"
- runs-on: ubuntu-20.04 - runs-on: ubuntu-20.04
arch: x64 arch: x64
@ -40,7 +40,7 @@ jobs:
- runs-on: macos-13 - runs-on: macos-13
arch: x64 arch: x64
cmake: "3.8" cmake: "3.15"
- runs-on: windows-2019 - runs-on: windows-2019
arch: x64 # x86 compilers seem to be missing on 2019 image arch: x64 # x86 compilers seem to be missing on 2019 image

View File

@ -5,6 +5,9 @@ on:
pull_request: pull_request:
branches: branches:
- master - master
- stable
- smart_holder
- v*
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
@ -23,8 +26,6 @@ jobs:
- uses: pypa/cibuildwheel@v2.20 - uses: pypa/cibuildwheel@v2.20
env: env:
PYODIDE_BUILD_EXPORTS: whole_archive PYODIDE_BUILD_EXPORTS: whole_archive
CFLAGS: -fexceptions
LDFLAGS: -fexceptions
with: with:
package-dir: tests package-dir: tests
only: cp312-pyodide_wasm32 only: cp312-pyodide_wasm32

View File

@ -103,7 +103,7 @@ jobs:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
- name: Generate artifact attestation for sdist and wheel - name: Generate artifact attestation for sdist and wheel
uses: actions/attest-build-provenance@210c1913531870065f03ce1f9440dd87bc0938cd # v1.4.0 uses: actions/attest-build-provenance@310b0a4a3b0b78ef57ecda988ee04b132db73ef8 # v1.4.1
with: with:
subject-path: "*/pybind11*" subject-path: "*/pybind11*"

View File

@ -10,16 +10,7 @@ if(NOT CMAKE_VERSION VERSION_LESS "3.27")
cmake_policy(GET CMP0148 _pybind11_cmp0148) cmake_policy(GET CMP0148 _pybind11_cmp0148)
endif() endif()
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
if(_pybind11_cmp0148) if(_pybind11_cmp0148)
cmake_policy(SET CMP0148 ${_pybind11_cmp0148}) cmake_policy(SET CMP0148 ${_pybind11_cmp0148})
@ -27,9 +18,7 @@ if(_pybind11_cmp0148)
endif() endif()
# Avoid infinite recursion if tests include this as a subdirectory # Avoid infinite recursion if tests include this as a subdirectory
if(DEFINED PYBIND11_MASTER_PROJECT) include_guard(GLOBAL)
return()
endif()
# Extract project version from source # Extract project version from source
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h" file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h"
@ -74,14 +63,6 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
set(PYBIND11_MASTER_PROJECT ON) set(PYBIND11_MASTER_PROJECT ON)
if(OSX AND CMAKE_VERSION VERSION_LESS 3.7)
# Bug in macOS CMake < 3.7 is unable to download catch
message(WARNING "CMAKE 3.7+ needed on macOS to download catch, and newer HIGHLY recommended")
elseif(WINDOWS AND CMAKE_VERSION VERSION_LESS 3.8)
# Only tested with 3.8+ in CI.
message(WARNING "CMAKE 3.8+ tested on Windows, previous versions untested")
endif()
message(STATUS "CMake ${CMAKE_VERSION}") message(STATUS "CMake ${CMAKE_VERSION}")
if(CMAKE_CXX_STANDARD) if(CMAKE_CXX_STANDARD)
@ -133,8 +114,7 @@ cmake_dependent_option(
"Install pybind11 headers in Python include directory instead of default installation prefix" "Install pybind11 headers in Python include directory instead of default installation prefix"
OFF "PYBIND11_INSTALL" OFF) OFF "PYBIND11_INSTALL" OFF)
cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default} option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default})
"NOT CMAKE_VERSION VERSION_LESS 3.12" OFF)
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests # Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
# (makes transition easier while we support both modes). # (makes transition easier while we support both modes).
@ -154,7 +134,7 @@ set(PYBIND11_HEADERS
include/pybind11/detail/init.h include/pybind11/detail/init.h
include/pybind11/detail/internals.h include/pybind11/detail/internals.h
include/pybind11/detail/native_enum_data.h include/pybind11/detail/native_enum_data.h
include/pybind11/detail/smart_holder_poc.h include/pybind11/detail/struct_smart_holder.h
include/pybind11/detail/type_caster_base.h include/pybind11/detail/type_caster_base.h
include/pybind11/detail/typeid.h include/pybind11/detail/typeid.h
include/pybind11/detail/using_smart_holder.h include/pybind11/detail/using_smart_holder.h
@ -190,7 +170,7 @@ set(PYBIND11_HEADERS
include/pybind11/typing.h) include/pybind11/typing.h)
# Compare with grep and warn if mismatched # Compare with grep and warn if mismatched
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) if(PYBIND11_MASTER_PROJECT)
file( file(
GLOB_RECURSE _pybind11_header_check GLOB_RECURSE _pybind11_header_check
LIST_DIRECTORIES false LIST_DIRECTORIES false
@ -208,10 +188,7 @@ if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
endif() endif()
endif() endif()
# CMake 3.12 added list(TRANSFORM <list> PREPEND list(TRANSFORM PYBIND11_HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
# But we can't use it yet
string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" PYBIND11_HEADERS
"${PYBIND11_HEADERS}")
# Cache variable so this can be used in parent projects # Cache variable so this can be used in parent projects
set(pybind11_INCLUDE_DIR set(pybind11_INCLUDE_DIR
@ -281,25 +258,11 @@ if(PYBIND11_INSTALL)
tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
if(CMAKE_VERSION VERSION_LESS 3.14) # CMake natively supports header-only libraries
# Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does write_basic_package_version_file(
# not depend on architecture specific settings or libraries. ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) VERSION ${PROJECT_VERSION}
unset(CMAKE_SIZEOF_VOID_P) COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)
set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P})
else()
# CMake 3.14+ natively supports header-only libraries
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT)
endif()
install( install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake

View File

@ -259,7 +259,7 @@ copying to take place:
"small"_a // <- This one can be copied if needed "small"_a // <- This one can be copied if needed
); );
With the above binding code, attempting to call the the ``some_method(m)`` With the above binding code, attempting to call the ``some_method(m)``
method on a ``MyClass`` object, or attempting to call ``some_function(m, m2)`` method on a ``MyClass`` object, or attempting to call ``some_function(m, m2)``
will raise a ``RuntimeError`` rather than making a temporary copy of the array. will raise a ``RuntimeError`` rather than making a temporary copy of the array.
It will, however, allow the ``m2`` argument to be copied into a temporary if It will, however, allow the ``m2`` argument to be copied into a temporary if

View File

@ -162,7 +162,7 @@ the declaration
.. code-block:: cpp .. code-block:: cpp
PYBIND11_MAKE_OPAQUE(std::vector<int>); PYBIND11_MAKE_OPAQUE(std::vector<int>)
before any binding code (e.g. invocations to ``class_::def()``, etc.). This before any binding code (e.g. invocations to ``class_::def()``, etc.). This
macro must be specified at the top level (and outside of any namespaces), since macro must be specified at the top level (and outside of any namespaces), since
@ -207,8 +207,8 @@ The following example showcases usage of :file:`pybind11/stl_bind.h`:
// Don't forget this // Don't forget this
#include <pybind11/stl_bind.h> #include <pybind11/stl_bind.h>
PYBIND11_MAKE_OPAQUE(std::vector<int>); PYBIND11_MAKE_OPAQUE(std::vector<int>)
PYBIND11_MAKE_OPAQUE(std::map<std::string, double>); PYBIND11_MAKE_OPAQUE(std::map<std::string, double>)
// ... // ...

View File

@ -18,7 +18,7 @@ information, see :doc:`/compiling`.
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.29) cmake_minimum_required(VERSION 3.15...3.30)
project(example) project(example)
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)` find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`

View File

@ -131,7 +131,7 @@ top namespace level before any binding code:
.. code-block:: cpp .. code-block:: cpp
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>)
The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a
placeholder name that is used as a template parameter of the second argument. placeholder name that is used as a template parameter of the second argument.
@ -143,7 +143,7 @@ by default. Specify
.. code-block:: cpp .. code-block:: cpp
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>, true); PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>, true)
if ``SmartPtr<T>`` can always be initialized from a ``T*`` pointer without the if ``SmartPtr<T>`` can always be initialized from a ``T*`` pointer without the
risk of inconsistencies (such as multiple independent ``SmartPtr`` instances risk of inconsistencies (such as multiple independent ``SmartPtr`` instances
@ -161,7 +161,7 @@ specialized:
.. code-block:: cpp .. code-block:: cpp
// Always needed for custom holder types // Always needed for custom holder types
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>)
// Only needed if the type's `.get()` goes by another name // Only needed if the type's `.get()` goes by another name
namespace PYBIND11_NAMESPACE { namespace detail { namespace PYBIND11_NAMESPACE { namespace detail {

View File

@ -78,6 +78,13 @@ For brevity, all code examples assume that the following two lines are present:
namespace py = pybind11; namespace py = pybind11;
.. note::
``pybind11/pybind11.h`` includes ``Python.h``, as such it must be the first file
included in any source file or header for `the same reasons as Python.h`_.
.. _`the same reasons as Python.h`: https://docs.python.org/3/extending/extending.html#a-simple-example
Some features may require additional headers, but those will be specified as needed. Some features may require additional headers, but those will be specified as needed.
.. _simple_example: .. _simple_example:

View File

@ -15,6 +15,115 @@ IN DEVELOPMENT
Changes will be summarized here periodically. Changes will be summarized here periodically.
New Features:
* Support for Python 3.7 was removed. (Official end-of-life: 2023-06-27).
`#5191 <https://github.com/pybind/pybind11/pull/5191>`_
* stl.h ``list|set|map_caster`` were made more user friendly: it is no longer
necessary to explicitly convert Python iterables to ``tuple()``, ``set()``,
or ``map()`` in many common situations.
`#4686 <https://github.com/pybind/pybind11/pull/4686>`_
* Support for CMake older than 3.15 removed. CMake 3.15-3.30 supported.
`#5304 <https://github.com/pybind/pybind11/pull/5304>`_
* The ``array_caster`` in pybind11/stl.h was enhanced to support value types that are not default-constructible.
`#5305 <https://github.com/pybind/pybind11/pull/5305>`_
Version 2.13.5 (August 22, 2024)
--------------------------------
Bug fixes:
* Fix includes when using Windows long paths (``\\?\`` prefix).
`#5321 <https://github.com/pybind/pybind11/pull/5321>`_
* Support ``-Wpedantic`` in C++20 mode.
`#5322 <https://github.com/pybind/pybind11/pull/5322>`_
* Fix and test ``<ranges>`` support for ``py::tuple`` and ``py::list``.
`#5314 <https://github.com/pybind/pybind11/pull/5314>`_
Version 2.13.4 (August 14, 2024)
--------------------------------
Bug fixes:
* Fix paths with spaces, including on Windows.
(Replaces regression from `#5302 <https://github.com/pybind/pybind11/pull/5302>`_)
`#4874 <https://github.com/pybind/pybind11/pull/4874>`_
Documentation:
* Remove repetitive words.
`#5308 <https://github.com/pybind/pybind11/pull/5308>`_
Version 2.13.3 (August 13, 2024)
--------------------------------
Bug fixes:
* Quote paths from pybind11-config
`#5302 <https://github.com/pybind/pybind11/pull/5302>`_
* Fix typo in Emscripten support when in config mode (CMake)
`#5301 <https://github.com/pybind/pybind11/pull/5301>`_
Version 2.13.2 (August 13, 2024)
--------------------------------
New Features:
* A ``pybind11::detail::type_caster_std_function_specializations`` feature was added, to support specializations for
``std::function``'s with return types that require custom to-Python conversion behavior (to primary use case is to catch and
convert exceptions).
`#4597 <https://github.com/pybind/pybind11/pull/4597>`_
Changes:
* Use ``PyMutex`` instead of ``std::mutex`` for internal locking in the free-threaded build.
`#5219 <https://github.com/pybind/pybind11/pull/5219>`_
* Add a special type annotation for C++ empty tuple.
`#5214 <https://github.com/pybind/pybind11/pull/5214>`_
* When compiling for WebAssembly, add the required exception flags (CMake 3.13+).
`#5298 <https://github.com/pybind/pybind11/pull/5298>`_
Bug fixes:
* Make ``gil_safe_call_once_and_store`` thread-safe in free-threaded CPython.
`#5246 <https://github.com/pybind/pybind11/pull/5246>`_
* A missing ``#include <algorithm>`` in pybind11/typing.h was added to fix build errors (in case user code does not already depend
on that include).
`#5208 <https://github.com/pybind/pybind11/pull/5208>`_
* Fix regression introduced in #5201 for GCC<10.3 in C++20 mode.
`#5205 <https://github.com/pybind/pybind11/pull/5205>`_
.. fix(cmake)
* Remove extra = when assigning flto value in the case for Clang in CMake.
`#5207 <https://github.com/pybind/pybind11/pull/5207>`_
Tests:
* Adding WASM testing to our CI (Pyodide / Emscripten via scikit-build-core).
`#4745 <https://github.com/pybind/pybind11/pull/4745>`_
* clang-tidy (in GitHub Actions) was updated from clang 15 to clang 18.
`#5272 <https://github.com/pybind/pybind11/pull/5272>`_
Version 2.13.1 (June 26, 2024) Version 2.13.1 (June 26, 2024)
------------------------------ ------------------------------

View File

@ -18,7 +18,7 @@ A Python extension module can be created with just a few lines of code:
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.15...3.29) cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
set(PYBIND11_FINDPYTHON ON) set(PYBIND11_FINDPYTHON ON)
@ -319,11 +319,11 @@ Building with CMake
For C++ codebases that have an existing CMake-based build system, a Python For C++ codebases that have an existing CMake-based build system, a Python
extension module can be created with just a few lines of code, as seen above in extension module can be created with just a few lines of code, as seen above in
the module section. Pybind11 currently supports a lower minimum if you don't the module section. Pybind11 currently defaults to the old mechanism, though be
use the modern FindPython, though be aware that CMake 3.27 removed the old aware that CMake 3.27 removed the old mechanism, so pybind11 will automatically
mechanism, so pybind11 will automatically switch if the old mechanism is not switch if the old mechanism is not available. Please opt into the new mechanism
available. Please opt into the new mechanism if at all possible. Our default if at all possible. Our default may change in future versions. This is the
may change in future versions. This is the minimum required: minimum required:
@ -333,6 +333,9 @@ may change in future versions. This is the minimum required:
.. versionchanged:: 2.11 .. versionchanged:: 2.11
CMake 3.5+ is required. CMake 3.5+ is required.
.. versionchanged:: 2.14
CMake 3.15+ is required.
Further information can be found at :doc:`cmake/index`. Further information can be found at :doc:`cmake/index`.
@ -388,7 +391,7 @@ that will be respected instead of the built-in flag search.
The ``OPT_SIZE`` flag enables size-based optimization equivalent to the The ``OPT_SIZE`` flag enables size-based optimization equivalent to the
standard ``/Os`` or ``-Os`` compiler flags and the ``MinSizeRel`` build type, standard ``/Os`` or ``-Os`` compiler flags and the ``MinSizeRel`` build type,
which avoid optimizations that that can substantially increase the size of the which avoid optimizations that can substantially increase the size of the
resulting binary. This flag is particularly useful in projects that are split resulting binary. This flag is particularly useful in projects that are split
into performance-critical parts and associated bindings. In this case, we can into performance-critical parts and associated bindings. In this case, we can
compile the project in release mode (and hence, optimize performance globally), compile the project in release mode (and hence, optimize performance globally),
@ -444,7 +447,7 @@ See the `Config file`_ docstring for details of relevant CMake variables.
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.4...3.18) cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) find_package(pybind11 REQUIRED)
@ -483,14 +486,13 @@ can refer to the same [cmake_example]_ repository for a full sample project
FindPython mode FindPython mode
--------------- ---------------
CMake 3.12+ (3.15+ recommended, 3.18.2+ ideal) added a new module called Modern CMake (3.18.2+ ideal) added a new module called FindPython that had a
FindPython that had a highly improved search algorithm and modern targets highly improved search algorithm and modern targets and tools. If you use
and tools. If you use FindPython, pybind11 will detect this and use the FindPython, pybind11 will detect this and use the existing targets instead:
existing targets instead:
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.15...3.22) cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(Python 3.8 COMPONENTS Interpreter Development REQUIRED) find_package(Python 3.8 COMPONENTS Interpreter Development REQUIRED)
@ -541,7 +543,7 @@ available in all modes. The targets provided are:
Just the "linking" part of pybind11:module Just the "linking" part of pybind11:module
``pybind11::module`` ``pybind11::module``
Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper`` Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython) or ``pybind11::python_link_helper``
``pybind11::embed`` ``pybind11::embed``
Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs
@ -568,7 +570,7 @@ You can use these targets to build complex applications. For example, the
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.29) cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
@ -626,7 +628,7 @@ information about usage in C++, see :doc:`/advanced/embedding`.
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.29) cmake_minimum_required(VERSION 3.15...3.30)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
@ -719,7 +721,7 @@ customizable pybind11-based wrappers by parsing C++ header files.
[litgen]_ is an automatic python bindings generator with a focus on generating [litgen]_ is an automatic python bindings generator with a focus on generating
documented and discoverable bindings: bindings will nicely reproduce the documentation documented and discoverable bindings: bindings will nicely reproduce the documentation
found in headers. It is is based on srcML (srcml.org), a highly scalable, multi-language found in headers. It is based on srcML (srcml.org), a highly scalable, multi-language
parsing tool with a developer centric approach. The API that you want to expose to python parsing tool with a developer centric approach. The API that you want to expose to python
must be C++14 compatible (but your implementation can use more modern constructs). must be C++14 compatible (but your implementation can use more modern constructs).

View File

@ -258,9 +258,9 @@ CMake configure line. (Replace ``$(which python)`` with a path to python if
your prefer.) your prefer.)
You can alternatively try ``-DPYBIND11_FINDPYTHON=ON``, which will activate the You can alternatively try ``-DPYBIND11_FINDPYTHON=ON``, which will activate the
new CMake FindPython support instead of pybind11's custom search. Requires new CMake FindPython support instead of pybind11's custom search. Newer CMake,
CMake 3.12+, and 3.15+ or 3.18.2+ are even better. You can set this in your like, 3.18.2+, is recommended. You can set this in your ``CMakeLists.txt``
``CMakeLists.txt`` before adding or finding pybind11, as well. before adding or finding pybind11, as well.
Inconsistent detection of Python version in CMake and pybind11 Inconsistent detection of Python version in CMake and pybind11
============================================================== ==============================================================
@ -281,11 +281,11 @@ There are three possible solutions:
from CMake and rely on pybind11 in detecting Python version. If this is not from CMake and rely on pybind11 in detecting Python version. If this is not
possible, the CMake machinery should be called *before* including pybind11. possible, the CMake machinery should be called *before* including pybind11.
2. Set ``PYBIND11_FINDPYTHON`` to ``True`` or use ``find_package(Python 2. Set ``PYBIND11_FINDPYTHON`` to ``True`` or use ``find_package(Python
COMPONENTS Interpreter Development)`` on modern CMake (3.12+, 3.15+ better, COMPONENTS Interpreter Development)`` on modern CMake ( 3.18.2+ best).
3.18.2+ best). Pybind11 in these cases uses the new CMake FindPython instead Pybind11 in these cases uses the new CMake FindPython instead of the old,
of the old, deprecated search tools, and these modules are much better at deprecated search tools, and these modules are much better at finding the
finding the correct Python. If FindPythonLibs/Interp are not available correct Python. If FindPythonLibs/Interp are not available (CMake 3.27+),
(CMake 3.27+), then this will be ignored and FindPython will be used. then this will be ignored and FindPython will be used.
3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python. 3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python.
However, you will have to use the target-based system, and do more setup However, you will have to use the target-based system, and do more setup
yourself, because it does not know about or include things that depend on yourself, because it does not know about or include things that depend on

View File

@ -957,8 +957,12 @@ public:
using base::value; using base::value;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
return base::template load_impl<copyable_holder_caster<type, std::shared_ptr<type>>>( if (base::template load_impl<copyable_holder_caster<type, std::shared_ptr<type>>>(
src, convert); src, convert)) {
sh_load_helper.maybe_set_python_instance_is_alias(src);
return true;
}
return false;
} }
explicit operator std::shared_ptr<type> *() { explicit operator std::shared_ptr<type> *() {
@ -966,14 +970,14 @@ public:
pybind11_fail("Passing `std::shared_ptr<T> *` from Python to C++ is not supported " pybind11_fail("Passing `std::shared_ptr<T> *` from Python to C++ is not supported "
"(inherently unsafe)."); "(inherently unsafe).");
} }
return std::addressof(shared_ptr_holder); return std::addressof(shared_ptr_storage);
} }
explicit operator std::shared_ptr<type> &() { explicit operator std::shared_ptr<type> &() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
shared_ptr_holder = sh_load_helper.load_as_shared_ptr(value); shared_ptr_storage = sh_load_helper.load_as_shared_ptr(value);
} }
return shared_ptr_holder; return shared_ptr_storage;
} }
static handle static handle
@ -1013,12 +1017,13 @@ protected:
void load_value(value_and_holder &&v_h) { void load_value(value_and_holder &&v_h) {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
sh_load_helper.loaded_v_h = v_h; sh_load_helper.loaded_v_h = v_h;
sh_load_helper.was_populated = true;
value = sh_load_helper.get_void_ptr_or_nullptr(); value = sh_load_helper.get_void_ptr_or_nullptr();
return; return;
} }
if (v_h.holder_constructed()) { if (v_h.holder_constructed()) {
value = v_h.value_ptr(); value = v_h.value_ptr();
shared_ptr_holder = v_h.template holder<std::shared_ptr<type>>(); shared_ptr_storage = v_h.template holder<std::shared_ptr<type>>();
return; return;
} }
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) " throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
@ -1047,8 +1052,8 @@ protected:
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h; sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h;
} else { } else {
shared_ptr_holder shared_ptr_storage
= std::shared_ptr<type>(sub_caster.shared_ptr_holder, (type *) value); = std::shared_ptr<type>(sub_caster.shared_ptr_storage, (type *) value);
} }
return true; return true;
} }
@ -1058,8 +1063,8 @@ protected:
static bool try_direct_conversions(handle) { return false; } static bool try_direct_conversions(handle) { return false; }
std::shared_ptr<type> shared_ptr_holder;
smart_holder_type_caster_support::load_helper<remove_cv_t<type>> sh_load_helper; // Const2Mutbl smart_holder_type_caster_support::load_helper<remove_cv_t<type>> sh_load_helper; // Const2Mutbl
std::shared_ptr<type> shared_ptr_storage;
}; };
#endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
@ -1134,20 +1139,25 @@ public:
policy = return_value_policy::reference_internal; policy = return_value_policy::reference_internal;
} }
if (policy != return_value_policy::reference_internal) { if (policy != return_value_policy::reference_internal) {
throw cast_error("Invalid return_value_policy for unique_ptr&"); throw cast_error("Invalid return_value_policy for const unique_ptr&");
} }
return type_caster_base<type>::cast(src.get(), policy, parent); return type_caster_base<type>::cast(src.get(), policy, parent);
} }
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
return base::template load_impl< if (base::template load_impl<
move_only_holder_caster<type, std::unique_ptr<type, deleter>>>(src, convert); move_only_holder_caster<type, std::unique_ptr<type, deleter>>>(src, convert)) {
sh_load_helper.maybe_set_python_instance_is_alias(src);
return true;
}
return false;
} }
void load_value(value_and_holder &&v_h) { void load_value(value_and_holder &&v_h) {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
sh_load_helper.loaded_v_h = v_h; sh_load_helper.loaded_v_h = v_h;
sh_load_helper.loaded_v_h.type = typeinfo; sh_load_helper.loaded_v_h.type = typeinfo;
sh_load_helper.was_populated = true;
value = sh_load_helper.get_void_ptr_or_nullptr(); value = sh_load_helper.get_void_ptr_or_nullptr();
return; return;
} }
@ -1156,8 +1166,14 @@ public:
+ clean_type_id(typeinfo->cpptype->name()) + ")"); + clean_type_id(typeinfo->cpptype->name()) + ")");
} }
template <typename> template <typename T_>
using cast_op_type = std::unique_ptr<type, deleter>; using cast_op_type
= conditional_t<std::is_same<typename std::remove_volatile<T_>::type,
const std::unique_ptr<type, deleter> &>::value
|| std::is_same<typename std::remove_volatile<T_>::type,
const std::unique_ptr<const type, deleter> &>::value,
const std::unique_ptr<type, deleter> &,
std::unique_ptr<type, deleter>>;
explicit operator std::unique_ptr<type, deleter>() { explicit operator std::unique_ptr<type, deleter>() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) { if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
@ -1166,6 +1182,28 @@ public:
pybind11_fail("Expected to be UNREACHABLE: " __FILE__ ":" PYBIND11_TOSTRING(__LINE__)); pybind11_fail("Expected to be UNREACHABLE: " __FILE__ ":" PYBIND11_TOSTRING(__LINE__));
} }
explicit operator const std::unique_ptr<type, deleter> &() {
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
// Get shared_ptr to ensure that the Python object is not disowned elsewhere.
shared_ptr_storage = sh_load_helper.load_as_shared_ptr(value);
// Build a temporary unique_ptr that is meant to never expire.
unique_ptr_storage = std::shared_ptr<std::unique_ptr<type, deleter>>(
new std::unique_ptr<type, deleter>{
sh_load_helper.template load_as_const_unique_ptr<deleter>(
shared_ptr_storage.get())},
[](std::unique_ptr<type, deleter> *ptr) {
if (!ptr) {
pybind11_fail("FATAL: `const std::unique_ptr<T, D> &` was disowned "
"(EXPECT UNDEFINED BEHAVIOR).");
}
(void) ptr->release();
delete ptr;
});
return *unique_ptr_storage;
}
pybind11_fail("Expected to be UNREACHABLE: " __FILE__ ":" PYBIND11_TOSTRING(__LINE__));
}
bool try_implicit_casts(handle src, bool convert) { bool try_implicit_casts(handle src, bool convert) {
for (auto &cast : typeinfo->implicit_casts) { for (auto &cast : typeinfo->implicit_casts) {
move_only_holder_caster sub_caster(*cast.first); move_only_holder_caster sub_caster(*cast.first);
@ -1186,6 +1224,8 @@ public:
static bool try_direct_conversions(handle) { return false; } static bool try_direct_conversions(handle) { return false; }
smart_holder_type_caster_support::load_helper<remove_cv_t<type>> sh_load_helper; // Const2Mutbl smart_holder_type_caster_support::load_helper<remove_cv_t<type>> sh_load_helper; // Const2Mutbl
std::shared_ptr<type> shared_ptr_storage; // Serves as a pseudo lock.
std::shared_ptr<std::unique_ptr<type, deleter>> unique_ptr_storage;
}; };
#endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT #endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT

View File

@ -9,8 +9,8 @@
#pragma once #pragma once
#include "../attr.h" #include <pybind11/attr.h>
#include "../options.h" #include <pybind11/options.h>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)

View File

@ -479,6 +479,8 @@ PYBIND11_WARNING_POP
} }
\endrst */ \endrst */
PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments")
#define PYBIND11_MODULE(name, variable, ...) \ #define PYBIND11_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) \
PYBIND11_MAYBE_UNUSED; \ PYBIND11_MAYBE_UNUSED; \
@ -499,6 +501,7 @@ PYBIND11_WARNING_POP
PYBIND11_CATCH_INIT_EXCEPTIONS \ PYBIND11_CATCH_INIT_EXCEPTIONS \
} \ } \
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable)) void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
PYBIND11_WARNING_POP
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@ -554,7 +557,7 @@ enum class return_value_policy : uint8_t {
object without taking ownership similar to the above object without taking ownership similar to the above
return_value_policy::reference policy. In contrast to that policy, the return_value_policy::reference policy. In contrast to that policy, the
function or property's implicit this argument (called the parent) is function or property's implicit this argument (called the parent) is
considered to be the the owner of the return value (the child). considered to be the owner of the return value (the child).
pybind11 then couples the lifetime of the parent to the child via a pybind11 then couples the lifetime of the parent to the child via a
reference relationship that ensures that the parent cannot be garbage reference relationship that ensures that the parent cannot be garbage
collected while Python is still using the child. More advanced collected while Python is still using the child. More advanced
@ -637,6 +640,11 @@ struct instance {
bool simple_instance_registered : 1; bool simple_instance_registered : 1;
/// If true, get_internals().patients has an entry for this object /// If true, get_internals().patients has an entry for this object
bool has_patients : 1; bool has_patients : 1;
// Cannot use PYBIND11_INTERNALS_VERSION >= 6 here without refactoring.
#if PYBIND11_VERSION_MAJOR >= 3
/// If true, this Python object needs to be kept alive for the lifetime of the C++ value.
bool is_alias : 1;
#endif
/// Initializes all of the above type/values/holders data (but not the instance values /// Initializes all of the above type/values/holders data (but not the instance values
/// themselves) /// themselves)

View File

@ -12,10 +12,10 @@
#include "common.h" #include "common.h"
#if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) #if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# include "../gil.h" # include <pybind11/gil.h>
#endif #endif
#include "../pytypes.h" #include <pybind11/pytypes.h>
#include <exception> #include <exception>
#include <mutex> #include <mutex>

View File

@ -1,4 +1,4 @@
// Copyright (c) 2020-2021 The Pybind Development Team. // Copyright (c) 2020-2024 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a // All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file. // BSD-style license that can be found in the LICENSE file.
@ -44,16 +44,6 @@ Details:
* The `void_cast_raw_ptr` option is needed to make the `smart_holder` `vptr` * The `void_cast_raw_ptr` option is needed to make the `smart_holder` `vptr`
member invisible to the `shared_from_this` mechanism, in case the lifetime member invisible to the `shared_from_this` mechanism, in case the lifetime
of a `PyObject` is tied to the pointee. of a `PyObject` is tied to the pointee.
* Regarding `PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP` below:
This define serves as a marker for code that is NOT used
from smart_holder_type_casters.h, but is exercised only from
tests/pure_cpp/smart_holder_poc_test.cpp. The marked code was useful
mainly for bootstrapping the smart_holder work. At this stage, with
smart_holder_type_casters.h in production use (at Google) since around
February 2021, it could be moved from here to tests/pure_cpp/ (help welcome).
It will probably be best in most cases to add tests for new functionality
under test/test_class_sh_*.
*/ */
#pragma once #pragma once
@ -146,7 +136,6 @@ struct smart_holder {
bool vptr_is_external_shared_ptr : 1; bool vptr_is_external_shared_ptr : 1;
bool is_populated : 1; bool is_populated : 1;
bool is_disowned : 1; bool is_disowned : 1;
bool pointee_depends_on_holder_owner : 1; // SMART_HOLDER_WIP: See PR #2839.
// Design choice: smart_holder is movable but not copyable. // Design choice: smart_holder is movable but not copyable.
smart_holder(smart_holder &&) = default; smart_holder(smart_holder &&) = default;
@ -156,8 +145,7 @@ struct smart_holder {
smart_holder() smart_holder()
: vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false}, : vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false},
vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false}, vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false} {}
pointee_depends_on_holder_owner{false} {}
bool has_pointee() const { return vptr != nullptr; } bool has_pointee() const { return vptr != nullptr; }
@ -243,6 +231,24 @@ struct smart_holder {
vptr_del_ptr->armed_flag = armed_flag; vptr_del_ptr->armed_flag = armed_flag;
} }
// Caller is responsible for precondition: ensure_compatible_rtti_uqp_del<T, D>() must succeed.
template <typename T, typename D>
std::unique_ptr<D> extract_deleter(const char *context) const {
const auto *gd = std::get_deleter<guarded_delete>(vptr);
if (gd && gd->use_del_fun) {
const auto &custom_deleter_ptr = gd->del_fun.template target<custom_deleter<T, D>>();
if (custom_deleter_ptr == nullptr) {
throw std::runtime_error(
std::string("smart_holder::extract_deleter() precondition failure (") + context
+ ").");
}
static_assert(std::is_copy_constructible<D>::value,
"Required for compatibility with smart_holder functionality.");
return std::unique_ptr<D>(new D(custom_deleter_ptr->deleter));
}
return nullptr;
}
static smart_holder from_raw_ptr_unowned(void *raw_ptr) { static smart_holder from_raw_ptr_unowned(void *raw_ptr) {
smart_holder hld; smart_holder hld;
hld.vptr.reset(raw_ptr, [](void *) {}); hld.vptr.reset(raw_ptr, [](void *) {});
@ -256,26 +262,6 @@ struct smart_holder {
return static_cast<T *>(vptr.get()); return static_cast<T *>(vptr.get());
} }
#ifdef PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP // See comment near top.
template <typename T>
T &as_lvalue_ref() const {
static const char *context = "as_lvalue_ref";
ensure_is_populated(context);
ensure_has_pointee(context);
return *as_raw_ptr_unowned<T>();
}
#endif
#ifdef PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP // See comment near top.
template <typename T>
T &&as_rvalue_ref() const {
static const char *context = "as_rvalue_ref";
ensure_is_populated(context);
ensure_has_pointee(context);
return std::move(*as_raw_ptr_unowned<T>());
}
#endif
template <typename T> template <typename T>
static smart_holder from_raw_ptr_take_ownership(T *raw_ptr, bool void_cast_raw_ptr = false) { static smart_holder from_raw_ptr_take_ownership(T *raw_ptr, bool void_cast_raw_ptr = false) {
ensure_pointee_is_destructible<T>("from_raw_ptr_take_ownership"); ensure_pointee_is_destructible<T>("from_raw_ptr_take_ownership");
@ -319,16 +305,6 @@ struct smart_holder {
release_disowned(); release_disowned();
} }
#ifdef PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP // See comment near top.
template <typename T>
T *as_raw_ptr_release_ownership(const char *context = "as_raw_ptr_release_ownership") {
ensure_can_release_ownership(context);
T *raw_ptr = as_raw_ptr_unowned<T>();
release_ownership();
return raw_ptr;
}
#endif
template <typename T, typename D> template <typename T, typename D>
static smart_holder from_unique_ptr(std::unique_ptr<T, D> &&unq_ptr, static smart_holder from_unique_ptr(std::unique_ptr<T, D> &&unq_ptr,
void *void_ptr = nullptr) { void *void_ptr = nullptr) {
@ -351,19 +327,6 @@ struct smart_holder {
return hld; return hld;
} }
#ifdef PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP // See comment near top.
template <typename T, typename D = std::default_delete<T>>
std::unique_ptr<T, D> as_unique_ptr() {
static const char *context = "as_unique_ptr";
ensure_compatible_rtti_uqp_del<T, D>(context);
ensure_use_count_1(context);
T *raw_ptr = as_raw_ptr_unowned<T>();
release_ownership();
// KNOWN DEFECT (see PR #4850): Does not copy the deleter.
return std::unique_ptr<T, D>(raw_ptr);
}
#endif
template <typename T> template <typename T>
static smart_holder from_shared_ptr(std::shared_ptr<T> shd_ptr) { static smart_holder from_shared_ptr(std::shared_ptr<T> shd_ptr) {
smart_holder hld; smart_holder hld;

View File

@ -9,9 +9,10 @@
#pragma once #pragma once
#include "../gil.h" #include <pybind11/gil.h>
#include "../pytypes.h" #include <pybind11/pytypes.h>
#include "../trampoline_self_life_support.h" #include <pybind11/trampoline_self_life_support.h>
#include "common.h" #include "common.h"
#include "descr.h" #include "descr.h"
#include "dynamic_raw_ptr_cast_if_possible.h" #include "dynamic_raw_ptr_cast_if_possible.h"
@ -704,6 +705,15 @@ inline std::unique_ptr<T, D> unique_with_deleter(T *raw_ptr, std::unique_ptr<D>
template <typename T> template <typename T>
struct load_helper : value_and_holder_helper { struct load_helper : value_and_holder_helper {
bool was_populated = false;
bool python_instance_is_alias = false;
void maybe_set_python_instance_is_alias(handle src) {
if (was_populated) {
python_instance_is_alias = reinterpret_cast<instance *>(src.ptr())->is_alias;
}
}
static std::shared_ptr<T> make_shared_ptr_with_responsible_parent(T *raw_ptr, handle parent) { static std::shared_ptr<T> make_shared_ptr_with_responsible_parent(T *raw_ptr, handle parent) {
return std::shared_ptr<T>(raw_ptr, shared_ptr_parent_life_support(parent.ptr())); return std::shared_ptr<T>(raw_ptr, shared_ptr_parent_life_support(parent.ptr()));
} }
@ -724,7 +734,7 @@ struct load_helper : value_and_holder_helper {
throw std::runtime_error("Non-owning holder (load_as_shared_ptr)."); throw std::runtime_error("Non-owning holder (load_as_shared_ptr).");
} }
auto *type_raw_ptr = static_cast<T *>(void_raw_ptr); auto *type_raw_ptr = static_cast<T *>(void_raw_ptr);
if (hld.pointee_depends_on_holder_owner) { if (python_instance_is_alias) {
auto *vptr_gd_ptr = std::get_deleter<pybindit::memory::guarded_delete>(hld.vptr); auto *vptr_gd_ptr = std::get_deleter<pybindit::memory::guarded_delete>(hld.vptr);
if (vptr_gd_ptr != nullptr) { if (vptr_gd_ptr != nullptr) {
std::shared_ptr<void> released_ptr = vptr_gd_ptr->released_ptr.lock(); std::shared_ptr<void> released_ptr = vptr_gd_ptr->released_ptr.lock();
@ -778,28 +788,13 @@ struct load_helper : value_and_holder_helper {
auto *self_life_support auto *self_life_support
= dynamic_raw_ptr_cast_if_possible<trampoline_self_life_support>(raw_type_ptr); = dynamic_raw_ptr_cast_if_possible<trampoline_self_life_support>(raw_type_ptr);
if (self_life_support == nullptr && holder().pointee_depends_on_holder_owner) { if (self_life_support == nullptr && python_instance_is_alias) {
throw value_error("Alias class (also known as trampoline) does not inherit from " throw value_error("Alias class (also known as trampoline) does not inherit from "
"py::trampoline_self_life_support, therefore the ownership of this " "py::trampoline_self_life_support, therefore the ownership of this "
"instance cannot safely be transferred to C++."); "instance cannot safely be transferred to C++.");
} }
// Temporary variable to store the extracted deleter in. std::unique_ptr<D> extracted_deleter = holder().template extract_deleter<T, D>(context);
std::unique_ptr<D> extracted_deleter;
auto *gd = std::get_deleter<pybindit::memory::guarded_delete>(holder().vptr);
if (gd && gd->use_del_fun) { // Note the ensure_compatible_rtti_uqp_del<T, D>() call above.
// In smart_holder_poc, a custom deleter is always stored in a guarded delete.
// The guarded delete's std::function<void(void*)> actually points at the
// custom_deleter type, so we can verify it is of the custom deleter type and
// finally extract its deleter.
using custom_deleter_D = pybindit::memory::custom_deleter<T, D>;
const auto &custom_deleter_ptr = gd->del_fun.template target<custom_deleter_D>();
assert(custom_deleter_ptr != nullptr);
// Now that we have confirmed the type of the deleter matches the desired return
// value we can extract the function.
extracted_deleter = std::unique_ptr<D>(new D(std::move(custom_deleter_ptr->deleter)));
}
// Critical transfer-of-ownership section. This must stay together. // Critical transfer-of-ownership section. This must stay together.
if (self_life_support != nullptr) { if (self_life_support != nullptr) {
@ -819,6 +814,19 @@ struct load_helper : value_and_holder_helper {
return result; return result;
} }
// This assumes load_as_shared_ptr succeeded(), and the returned shared_ptr is still alive.
// The returned unique_ptr is meant to never expire (the behavior is undefined otherwise).
template <typename D>
std::unique_ptr<T, D>
load_as_const_unique_ptr(T *raw_type_ptr, const char *context = "load_as_const_unique_ptr") {
if (!have_holder()) {
return unique_with_deleter<T, D>(nullptr, std::unique_ptr<D>());
}
holder().template ensure_compatible_rtti_uqp_del<T, D>(context);
return unique_with_deleter<T, D>(
raw_type_ptr, std::move(holder().template extract_deleter<T, D>(context)));
}
}; };
PYBIND11_NAMESPACE_END(smart_holder_type_caster_support) PYBIND11_NAMESPACE_END(smart_holder_type_caster_support)

View File

@ -10,7 +10,7 @@
#include <type_traits> #include <type_traits>
#ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT #ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
# include "smart_holder_poc.h" # include "struct_smart_holder.h"
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)

View File

@ -9,7 +9,8 @@
#pragma once #pragma once
#include "../numpy.h" #include <pybind11/numpy.h>
#include "common.h" #include "common.h"
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem. /* HINT: To suppress warnings originating from the Eigen headers, use -isystem.

View File

@ -7,7 +7,8 @@
#pragma once #pragma once
#include "../numpy.h" #include <pybind11/numpy.h>
#include "common.h" #include "common.h"
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)

View File

@ -9,12 +9,55 @@
#pragma once #pragma once
#define PYBIND11_HAS_TYPE_CASTER_STD_FUNCTION_SPECIALIZATIONS
#include "pybind11.h" #include "pybind11.h"
#include <functional> #include <functional>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
PYBIND11_NAMESPACE_BEGIN(type_caster_std_function_specializations)
// ensure GIL is held during functor destruction
struct func_handle {
function 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;
f = f_.f;
return *this;
}
~func_handle() {
gil_scoped_acquire acq;
function kill_f(std::move(f));
}
};
// to emulate 'move initialization capture' in C++11
struct func_wrapper_base {
func_handle hfunc;
explicit func_wrapper_base(func_handle &&hf) noexcept : hfunc(hf) {}
};
template <typename Return, typename... Args>
struct func_wrapper : func_wrapper_base {
using func_wrapper_base::func_wrapper_base;
Return operator()(Args... args) const {
gil_scoped_acquire acq;
// casts the returned object as a rvalue to the return type
return hfunc.f(std::forward<Args>(args)...).template cast<Return>();
}
};
PYBIND11_NAMESPACE_END(type_caster_std_function_specializations)
template <typename Return, typename... Args> template <typename Return, typename... Args>
struct type_caster<std::function<Return(Args...)>> { struct type_caster<std::function<Return(Args...)>> {
@ -77,40 +120,8 @@ public:
// See PR #1413 for full details // See PR #1413 for full details
} }
// ensure GIL is held during functor destruction value = type_caster_std_function_specializations::func_wrapper<Return, Args...>(
struct func_handle { type_caster_std_function_specializations::func_handle(std::move(func)));
function 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;
f = f_.f;
return *this;
}
~func_handle() {
gil_scoped_acquire acq;
function kill_f(std::move(f));
}
};
// to emulate 'move initialization capture' in C++11
struct func_wrapper {
func_handle hfunc;
explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
Return operator()(Args... args) const {
gil_scoped_acquire acq;
// casts the returned object as a rvalue to the return type
return hfunc.f(std::forward<Args>(args)...).template cast<Return>();
}
};
value = func_wrapper(func_handle(std::move(func)));
return true; return true;
} }

View File

@ -2278,7 +2278,8 @@ private:
} }
auto *uninitialized_location = std::addressof(v_h.holder<holder_type>()); auto *uninitialized_location = std::addressof(v_h.holder<holder_type>());
auto *value_ptr_w_t = v_h.value_ptr<type>(); auto *value_ptr_w_t = v_h.value_ptr<type>();
bool pointee_depends_on_holder_owner // Try downcast from `type` to `type_alias`:
inst->is_alias
= detail::dynamic_raw_ptr_cast_if_possible<type_alias>(value_ptr_w_t) != nullptr; = detail::dynamic_raw_ptr_cast_if_possible<type_alias>(value_ptr_w_t) != nullptr;
if (holder_void_ptr) { if (holder_void_ptr) {
// Note: inst->owned ignored. // Note: inst->owned ignored.
@ -2288,14 +2289,12 @@ private:
uninitialized_location, value_ptr_w_t, value_ptr_w_t)) { uninitialized_location, value_ptr_w_t, value_ptr_w_t)) {
if (inst->owned) { if (inst->owned) {
new (uninitialized_location) holder_type(holder_type::from_raw_ptr_take_ownership( new (uninitialized_location) holder_type(holder_type::from_raw_ptr_take_ownership(
value_ptr_w_t, /*void_cast_raw_ptr*/ pointee_depends_on_holder_owner)); value_ptr_w_t, /*void_cast_raw_ptr*/ inst->is_alias));
} else { } else {
new (uninitialized_location) new (uninitialized_location)
holder_type(holder_type::from_raw_ptr_unowned(value_ptr_w_t)); holder_type(holder_type::from_raw_ptr_unowned(value_ptr_w_t));
} }
} }
v_h.holder<holder_type>().pointee_depends_on_holder_owner
= pointee_depends_on_holder_owner;
v_h.set_holder_constructed(); v_h.set_holder_constructed();
} }

View File

@ -1263,6 +1263,7 @@ protected:
using pointer = arrow_proxy<const handle>; using pointer = arrow_proxy<const handle>;
sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) {} sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) {}
sequence_fast_readonly() = default;
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263 // NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference dereference() const { return *ptr; } reference dereference() const { return *ptr; }
@ -1285,6 +1286,7 @@ protected:
using pointer = arrow_proxy<const sequence_accessor>; using pointer = arrow_proxy<const sequence_accessor>;
sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) {} sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) {}
sequence_slow_readwrite() = default;
reference dereference() const { return {obj, static_cast<size_t>(index)}; } reference dereference() const { return {obj, static_cast<size_t>(index)}; }
void increment() { ++index; } void increment() { ++index; }

View File

@ -11,10 +11,14 @@
#include "pybind11.h" #include "pybind11.h"
#include "detail/common.h" #include "detail/common.h"
#include "detail/descr.h"
#include "detail/type_caster_base.h"
#include <deque> #include <deque>
#include <initializer_list>
#include <list> #include <list>
#include <map> #include <map>
#include <memory>
#include <ostream> #include <ostream>
#include <set> #include <set>
#include <unordered_map> #include <unordered_map>
@ -35,6 +39,89 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
//
// Begin: Equivalent of
// https://github.com/google/clif/blob/ae4eee1de07cdf115c0c9bf9fec9ff28efce6f6c/clif/python/runtime.cc#L388-L438
/*
The three `PyObjectTypeIsConvertibleTo*()` functions below are
the result of converging the behaviors of pybind11 and PyCLIF
(http://github.com/google/clif).
Originally PyCLIF was extremely far on the permissive side of the spectrum,
while pybind11 was very far on the strict side. Originally PyCLIF accepted any
Python iterable as input for a C++ `vector`/`set`/`map` argument, as long as
the elements were convertible. The obvious (in hindsight) problem was that
any empty Python iterable could be passed to any of these C++ types, e.g. `{}`
was accepted for C++ `vector`/`set` arguments, or `[]` for C++ `map` arguments.
The functions below strike a practical permissive-vs-strict compromise,
informed by tens of thousands of use cases in the wild. A main objective is
to prevent accidents and improve readability:
- Python literals must match the C++ types.
- For C++ `set`: The potentially reducing conversion from a Python sequence
(e.g. Python `list` or `tuple`) to a C++ `set` must be explicit, by going
through a Python `set`.
- However, a Python `set` can still be passed to a C++ `vector`. The rationale
is that this conversion is not reducing. Implicit conversions of this kind
are also fairly commonly used, therefore enforcing explicit conversions
would have an unfavorable cost : benefit ratio; more sloppily speaking,
such an enforcement would be more annoying than helpful.
*/
inline bool PyObjectIsInstanceWithOneOfTpNames(PyObject *obj,
std::initializer_list<const char *> tp_names) {
if (PyType_Check(obj)) {
return false;
}
const char *obj_tp_name = Py_TYPE(obj)->tp_name;
for (const auto *tp_name : tp_names) {
if (std::strcmp(obj_tp_name, tp_name) == 0) {
return true;
}
}
return false;
}
inline bool PyObjectTypeIsConvertibleToStdVector(PyObject *obj) {
if (PySequence_Check(obj) != 0) {
return !PyUnicode_Check(obj) && !PyBytes_Check(obj);
}
return (PyGen_Check(obj) != 0) || (PyAnySet_Check(obj) != 0)
|| PyObjectIsInstanceWithOneOfTpNames(
obj, {"dict_keys", "dict_values", "dict_items", "map", "zip"});
}
inline bool PyObjectTypeIsConvertibleToStdSet(PyObject *obj) {
return (PyAnySet_Check(obj) != 0) || PyObjectIsInstanceWithOneOfTpNames(obj, {"dict_keys"});
}
inline bool PyObjectTypeIsConvertibleToStdMap(PyObject *obj) {
if (PyDict_Check(obj)) {
return true;
}
// Implicit requirement in the conditions below:
// A type with `.__getitem__()` & `.items()` methods must implement these
// to be compatible with https://docs.python.org/3/c-api/mapping.html
if (PyMapping_Check(obj) == 0) {
return false;
}
PyObject *items = PyObject_GetAttrString(obj, "items");
if (items == nullptr) {
PyErr_Clear();
return false;
}
bool is_convertible = (PyCallable_Check(items) != 0);
Py_DECREF(items);
return is_convertible;
}
//
// End: Equivalent of clif/python/runtime.cc
//
/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for /// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for
/// forwarding a container element). Typically used indirect via forwarded_type(), below. /// forwarding a container element). Typically used indirect via forwarded_type(), below.
template <typename T, typename U> template <typename T, typename U>
@ -66,17 +153,10 @@ private:
} }
void reserve_maybe(const anyset &, void *) {} void reserve_maybe(const anyset &, void *) {}
public: bool convert_iterable(const iterable &itbl, bool convert) {
bool load(handle src, bool convert) { for (const auto &it : itbl) {
if (!isinstance<anyset>(src)) {
return false;
}
auto s = reinterpret_borrow<anyset>(src);
value.clear();
reserve_maybe(s, &value);
for (auto entry : s) {
key_conv conv; key_conv conv;
if (!conv.load(entry, convert)) { if (!conv.load(it, convert)) {
return false; return false;
} }
value.insert(cast_op<Key &&>(std::move(conv))); value.insert(cast_op<Key &&>(std::move(conv)));
@ -84,6 +164,29 @@ public:
return true; return true;
} }
bool convert_anyset(anyset s, bool convert) {
value.clear();
reserve_maybe(s, &value);
return convert_iterable(s, convert);
}
public:
bool load(handle src, bool convert) {
if (!PyObjectTypeIsConvertibleToStdSet(src.ptr())) {
return false;
}
if (isinstance<anyset>(src)) {
value.clear();
return convert_anyset(reinterpret_borrow<anyset>(src), convert);
}
if (!convert) {
return false;
}
assert(isinstance<iterable>(src));
value.clear();
return convert_iterable(reinterpret_borrow<iterable>(src), convert);
}
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
if (!std::is_lvalue_reference<T>::value) { if (!std::is_lvalue_reference<T>::value) {
@ -115,15 +218,10 @@ private:
} }
void reserve_maybe(const dict &, void *) {} void reserve_maybe(const dict &, void *) {}
public: bool convert_elements(const dict &d, bool convert) {
bool load(handle src, bool convert) {
if (!isinstance<dict>(src)) {
return false;
}
auto d = reinterpret_borrow<dict>(src);
value.clear(); value.clear();
reserve_maybe(d, &value); reserve_maybe(d, &value);
for (auto it : d) { for (const auto &it : d) {
key_conv kconv; key_conv kconv;
value_conv vconv; 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)) {
@ -134,6 +232,25 @@ public:
return true; return true;
} }
public:
bool load(handle src, bool convert) {
if (!PyObjectTypeIsConvertibleToStdMap(src.ptr())) {
return false;
}
if (isinstance<dict>(src)) {
return convert_elements(reinterpret_borrow<dict>(src), convert);
}
if (!convert) {
return false;
}
auto items = reinterpret_steal<object>(PyMapping_Items(src.ptr()));
if (!items) {
throw error_already_set();
}
assert(isinstance<iterable>(items));
return convert_elements(dict(reinterpret_borrow<iterable>(items)), convert);
}
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
dict d; dict d;
@ -166,20 +283,21 @@ struct list_caster {
using value_conv = make_caster<Value>; using value_conv = make_caster<Value>;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src)) { if (!PyObjectTypeIsConvertibleToStdVector(src.ptr())) {
return false; return false;
} }
auto s = reinterpret_borrow<sequence>(src); if (isinstance<sequence>(src)) {
value.clear(); return convert_elements(src, convert);
reserve_maybe(s, &value);
for (const auto &it : s) {
value_conv conv;
if (!conv.load(it, convert)) {
return false;
}
value.push_back(cast_op<Value &&>(std::move(conv)));
} }
return true; if (!convert) {
return false;
}
// Designed to be behavior-equivalent to passing tuple(src) from Python:
// The conversion to a tuple will first exhaust the generator object, to ensure that
// the generator is not left in an unpredictable (to the caller) partially-consumed
// state.
assert(isinstance<iterable>(src));
return convert_elements(tuple(reinterpret_borrow<iterable>(src)), convert);
} }
private: private:
@ -189,6 +307,20 @@ private:
} }
void reserve_maybe(const sequence &, void *) {} void reserve_maybe(const sequence &, void *) {}
bool convert_elements(handle seq, bool convert) {
auto s = reinterpret_borrow<sequence>(seq);
value.clear();
reserve_maybe(s, &value);
for (const auto &it : seq) {
value_conv conv;
if (!conv.load(it, convert)) {
return false;
}
value.push_back(cast_op<Value &&>(std::move(conv)));
}
return true;
}
public: public:
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
@ -220,43 +352,87 @@ struct type_caster<std::deque<Type, Alloc>> : list_caster<std::deque<Type, Alloc
template <typename Type, typename Alloc> template <typename Type, typename Alloc>
struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {}; struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {};
template <typename ArrayType, typename V, size_t... I>
ArrayType vector_to_array_impl(V &&v, index_sequence<I...>) {
return {{std::move(v[I])...}};
}
// Based on https://en.cppreference.com/w/cpp/container/array/to_array
template <typename ArrayType, size_t N, typename V>
ArrayType vector_to_array(V &&v) {
return vector_to_array_impl<ArrayType, V>(std::forward<V>(v), make_index_sequence<N>{});
}
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0> template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0>
struct array_caster { struct array_caster {
using value_conv = make_caster<Value>; using value_conv = make_caster<Value>;
private: private:
template <bool R = Resizable> std::unique_ptr<ArrayType> value;
bool require_size(enable_if_t<R, size_t> size) {
if (value.size() != size) {
value.resize(size);
}
return true;
}
template <bool R = Resizable>
bool require_size(enable_if_t<!R, size_t> size) {
return size == Size;
}
public: template <bool R = Resizable, enable_if_t<R, int> = 0>
bool load(handle src, bool convert) { bool convert_elements(handle seq, bool convert) {
if (!isinstance<sequence>(src)) { auto l = reinterpret_borrow<sequence>(seq);
return false; value.reset(new ArrayType{});
} // Using `resize` to preserve the behavior exactly as it was before PR #5305
auto l = reinterpret_borrow<sequence>(src); // For the `resize` to work, `Value` must be default constructible.
if (!require_size(l.size())) { // For `std::valarray`, this is a requirement:
return false; // https://en.cppreference.com/w/cpp/named_req/NumericType
} value->resize(l.size());
size_t ctr = 0; size_t ctr = 0;
for (const auto &it : l) { for (const auto &it : l) {
value_conv conv; value_conv conv;
if (!conv.load(it, convert)) { if (!conv.load(it, convert)) {
return false; return false;
} }
value[ctr++] = cast_op<Value &&>(std::move(conv)); (*value)[ctr++] = cast_op<Value &&>(std::move(conv));
} }
return true; return true;
} }
template <bool R = Resizable, enable_if_t<!R, int> = 0>
bool convert_elements(handle seq, bool convert) {
auto l = reinterpret_borrow<sequence>(seq);
if (l.size() != Size) {
return false;
}
// The `temp` storage is needed to support `Value` types that are not
// default-constructible.
// Deliberate choice: no template specializations, for simplicity, and
// because the compile time overhead for the specializations is deemed
// more significant than the runtime overhead for the `temp` storage.
std::vector<Value> temp;
temp.reserve(l.size());
for (auto it : l) {
value_conv conv;
if (!conv.load(it, convert)) {
return false;
}
temp.emplace_back(cast_op<Value &&>(std::move(conv)));
}
value.reset(new ArrayType(vector_to_array<ArrayType, Size>(std::move(temp))));
return true;
}
public:
bool load(handle src, bool convert) {
if (!PyObjectTypeIsConvertibleToStdVector(src.ptr())) {
return false;
}
if (isinstance<sequence>(src)) {
return convert_elements(src, convert);
}
if (!convert) {
return false;
}
// Designed to be behavior-equivalent to passing tuple(src) from Python:
// The conversion to a tuple will first exhaust the generator object, to ensure that
// the generator is not left in an unpredictable (to the caller) partially-consumed
// state.
assert(isinstance<iterable>(src));
return convert_elements(tuple(reinterpret_borrow<iterable>(src)), convert);
}
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size()); list l(src.size());
@ -272,12 +448,36 @@ public:
return l.release(); return l.release();
} }
PYBIND11_TYPE_CASTER(ArrayType, // Code copied from PYBIND11_TYPE_CASTER macro.
const_name<Resizable>(const_name(""), const_name("Annotated[")) // Intentionally preserving the behavior exactly as it was before PR #5305
+ const_name("list[") + value_conv::name + const_name("]") template <typename T_, enable_if_t<std::is_same<ArrayType, remove_cv_t<T_>>::value, int> = 0>
+ const_name<Resizable>(const_name(""), static handle cast(T_ *src, return_value_policy policy, handle parent) {
const_name(", FixedSize(") if (!src) {
+ const_name<Size>() + const_name(")]"))); return none().release();
}
if (policy == return_value_policy::take_ownership) {
auto h = cast(std::move(*src), policy, parent);
delete src; // WARNING: Assumes `src` was allocated with `new`.
return h;
}
return cast(*src, policy, parent);
}
// NOLINTNEXTLINE(google-explicit-constructor)
operator ArrayType *() { return &(*value); }
// NOLINTNEXTLINE(google-explicit-constructor)
operator ArrayType &() { return *value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator ArrayType &&() && { return std::move(*value); }
template <typename T_>
using cast_op_type = movable_cast_op_type<T_>;
static constexpr auto name
= const_name<Resizable>(const_name(""), const_name("Annotated[")) + const_name("list[")
+ value_conv::name + const_name("]")
+ const_name<Resizable>(
const_name(""), const_name(", FixedSize(") + const_name<Size>() + const_name(")]"));
}; };
template <typename Type, size_t Size> template <typename Type, size_t Size>

View File

@ -4,11 +4,11 @@
#pragma once #pragma once
#include "../pybind11.h" #include <pybind11/cast.h>
#include "../detail/common.h" #include <pybind11/detail/common.h>
#include "../detail/descr.h" #include <pybind11/detail/descr.h>
#include "../cast.h" #include <pybind11/pybind11.h>
#include "../pytypes.h" #include <pybind11/pytypes.h>
#include <string> #include <string>

View File

@ -2,12 +2,35 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import re
import sys import sys
import sysconfig import sysconfig
from ._version import __version__ from ._version import __version__
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
# This is the conditional used for os.path being posixpath
if "posix" in sys.builtin_module_names:
from shlex import quote
elif "nt" in sys.builtin_module_names:
# See https://github.com/mesonbuild/meson/blob/db22551ed9d2dd7889abea01cc1c7bba02bf1c75/mesonbuild/utils/universal.py#L1092-L1121
# and the original documents:
# https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments and
# https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
UNSAFE = re.compile("[ \t\n\r]")
def quote(s: str) -> str:
if s and not UNSAFE.search(s):
return s
# Paths cannot contain a '"' on Windows, so we don't need to worry
# about nuanced counting here.
return f'"{s}\\"' if s.endswith("\\") else f'"{s}"'
else:
def quote(s: str) -> str:
return s
def print_includes() -> None: def print_includes() -> None:
dirs = [ dirs = [
@ -22,7 +45,7 @@ def print_includes() -> None:
if d and d not in unique_dirs: if d and d not in unique_dirs:
unique_dirs.append(d) unique_dirs.append(d)
print(" ".join("-I" + d for d in unique_dirs)) print(" ".join(quote(f"-I{d}") for d in unique_dirs))
def main() -> None: def main() -> None:
@ -54,9 +77,9 @@ def main() -> None:
if args.includes: if args.includes:
print_includes() print_includes()
if args.cmakedir: if args.cmakedir:
print(get_cmake_dir()) print(quote(get_cmake_dir()))
if args.pkgconfigdir: if args.pkgconfigdir:
print(get_pkgconfig_dir()) print(quote(get_pkgconfig_dir()))
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -5,16 +5,7 @@
# All rights reserved. Use of this source code is governed by a # All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file. # BSD-style license that can be found in the LICENSE file.
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
# Filter out items; print an optional message if any items filtered. This ignores extensions. # Filter out items; print an optional message if any items filtered. This ignores extensions.
# #
@ -76,8 +67,8 @@ project(pybind11_tests CXX)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools")
option(PYBIND11_WERROR "Report all warnings as errors" OFF) option(PYBIND11_WERROR "Report all warnings as errors" OFF)
option(DOWNLOAD_EIGEN "Download EIGEN (requires CMake 3.11+)" OFF) option(DOWNLOAD_EIGEN "Download EIGEN" OFF)
option(PYBIND11_CUDA_TESTS "Enable building CUDA tests (requires CMake 3.12+)" OFF) option(PYBIND11_CUDA_TESTS "Enable building CUDA tests" OFF)
set(PYBIND11_TEST_OVERRIDE set(PYBIND11_TEST_OVERRIDE
"" ""
CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests") CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests")
@ -130,7 +121,6 @@ set(PYBIND11_TEST_FILES
test_class_sh_factory_constructors test_class_sh_factory_constructors
test_class_sh_inheritance test_class_sh_inheritance
test_class_sh_mi_thunks test_class_sh_mi_thunks
test_class_sh_module_local.py
test_class_sh_property test_class_sh_property
test_class_sh_property_non_owning test_class_sh_property_non_owning
test_class_sh_shared_ptr_copy_move test_class_sh_shared_ptr_copy_move
@ -178,6 +168,7 @@ set(PYBIND11_TEST_FILES
test_tagbased_polymorphic test_tagbased_polymorphic
test_thread test_thread
test_type_caster_pyobject_ptr test_type_caster_pyobject_ptr
test_type_caster_std_function_specializations
test_union test_union
test_unnamed_namespace_a test_unnamed_namespace_a
test_unnamed_namespace_b test_unnamed_namespace_b
@ -245,8 +236,6 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_
# And add additional targets for other tests. # And add additional targets for other tests.
tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set") tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set")
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils") tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
tests_extra_targets("test_class_sh_module_local.py"
"class_sh_module_local_0;class_sh_module_local_1;class_sh_module_local_2")
set(PYBIND11_EIGEN_REPO set(PYBIND11_EIGEN_REPO
"https://gitlab.com/libeigen/eigen.git" "https://gitlab.com/libeigen/eigen.git"
@ -270,25 +259,21 @@ endif()
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
# Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake). # Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake).
# Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also # Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
# produces a fatal error if loaded from a pre-3.0 cmake.
if(DOWNLOAD_EIGEN) if(DOWNLOAD_EIGEN)
if(CMAKE_VERSION VERSION_LESS 3.11) if(CMAKE_VERSION VERSION_LESS 3.18)
message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN") set(_opts)
else()
set(_opts SOURCE_SUBDIR no-cmake-build)
endif() endif()
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
eigen eigen
GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}" GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}") GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}"
${_opts})
FetchContent_GetProperties(eigen) FetchContent_MakeAvailable(eigen)
if(NOT eigen_POPULATED) if(NOT CMAKE_VERSION VERSION_LESS 3.18)
message( set(EIGEN3_INCLUDE_DIR "${eigen_SOURCE_DIR}")
STATUS
"Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}"
)
FetchContent_Populate(eigen)
endif() endif()
set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR})
@ -336,8 +321,7 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I}) list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
endif() endif()
message( message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON to download")
STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download")
endif() endif()
endif() endif()
@ -417,6 +401,9 @@ function(pybind11_enable_warnings target_name)
-Wdeprecated -Wdeprecated
-Wundef -Wundef
-Wnon-virtual-dtor) -Wnon-virtual-dtor)
if(DEFINED CMAKE_CXX_STANDARD AND NOT CMAKE_CXX_STANDARD VERSION_LESS 20)
target_compile_options(${target_name} PRIVATE -Wpedantic)
endif()
endif() endif()
if(PYBIND11_WERROR) if(PYBIND11_WERROR)
@ -522,12 +509,10 @@ foreach(target ${test_targets})
endforeach() endforeach()
# Provide nice organisation in IDEs # Provide nice organisation in IDEs
if(NOT CMAKE_VERSION VERSION_LESS 3.8) source_group(
source_group( TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include"
TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include" PREFIX "Header Files"
PREFIX "Header Files" FILES ${PYBIND11_HEADERS})
FILES ${PYBIND11_HEADERS})
endif()
# Make sure pytest is found or produce a warning # Make sure pytest is found or produce a warning
pybind11_find_import(pytest VERSION 3.1) pybind11_find_import(pytest VERSION 3.1)

View File

@ -1,34 +0,0 @@
#include <pybind11/smart_holder.h>
#include <string>
namespace pybind11_tests {
namespace class_sh_module_local {
struct atyp { // Short for "any type".
std::string mtxt;
};
std::string get_mtxt(const atyp &obj) { return obj.mtxt; }
atyp rtrn_valu_atyp() { return atyp(); }
} // namespace class_sh_module_local
} // namespace pybind11_tests
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp)
PYBIND11_MODULE(class_sh_module_local_0, m) {
m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
#ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
false;
#else
true;
using namespace pybind11_tests::class_sh_module_local;
m.def("get_mtxt", get_mtxt);
m.def("rtrn_valu_atyp", rtrn_valu_atyp);
#endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
}

View File

@ -1,40 +0,0 @@
// Identical to class_sh_module_local_2.cpp, except 2 replaced with 1.
#include <pybind11/smart_holder.h>
#include <string>
namespace pybind11_tests {
namespace class_sh_module_local {
struct atyp { // Short for "any type".
std::string mtxt;
};
std::string get_mtxt(const atyp &obj) { return obj.mtxt; }
} // namespace class_sh_module_local
} // namespace pybind11_tests
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp)
PYBIND11_MODULE(class_sh_module_local_1, m) {
m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
#ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
false;
#else
true;
namespace py = pybind11;
using namespace pybind11_tests::class_sh_module_local;
py::classh<atyp>(m, "atyp", py::module_local())
.def(py::init([](const std::string &mtxt) {
atyp obj;
obj.mtxt = mtxt;
return obj;
}))
.def("tag", [](const atyp &) { return 1; });
m.def("get_mtxt", get_mtxt);
#endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
}

View File

@ -1,40 +0,0 @@
// Identical to class_sh_module_local_1.cpp, except 1 replaced with 2.
#include <pybind11/smart_holder.h>
#include <string>
namespace pybind11_tests {
namespace class_sh_module_local {
struct atyp { // Short for "any type".
std::string mtxt;
};
std::string get_mtxt(const atyp &obj) { return obj.mtxt; }
} // namespace class_sh_module_local
} // namespace pybind11_tests
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp)
PYBIND11_MODULE(class_sh_module_local_2, m) {
m.attr("defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
#ifndef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
false;
#else
true;
namespace py = pybind11;
using namespace pybind11_tests::class_sh_module_local;
py::classh<atyp>(m, "atyp", py::module_local())
.def(py::init([](const std::string &mtxt) {
atyp obj;
obj.mtxt = mtxt;
return obj;
}))
.def("tag", [](const atyp &) { return 2; });
m.def("get_mtxt", get_mtxt);
#endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
}

View File

@ -9,7 +9,6 @@ import tarfile
import zipfile import zipfile
# These tests must be run explicitly # These tests must be run explicitly
# They require CMake 3.15+ (--install)
DIR = os.path.abspath(os.path.dirname(__file__)) DIR = os.path.abspath(os.path.dirname(__file__))
MAIN_DIR = os.path.dirname(os.path.dirname(DIR)) MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
@ -61,7 +60,7 @@ detail_headers = {
"include/pybind11/detail/init.h", "include/pybind11/detail/init.h",
"include/pybind11/detail/internals.h", "include/pybind11/detail/internals.h",
"include/pybind11/detail/native_enum_data.h", "include/pybind11/detail/native_enum_data.h",
"include/pybind11/detail/smart_holder_poc.h", "include/pybind11/detail/struct_smart_holder.h",
"include/pybind11/detail/type_caster_base.h", "include/pybind11/detail/type_caster_base.h",
"include/pybind11/detail/typeid.h", "include/pybind11/detail/typeid.h",
"include/pybind11/detail/using_smart_holder.h", "include/pybind11/detail/using_smart_holder.h",

View File

@ -56,13 +56,13 @@ private:
std::string message = ""; std::string message = "";
}; };
PYBIND11_MAKE_OPAQUE(LocalVec); PYBIND11_MAKE_OPAQUE(LocalVec)
PYBIND11_MAKE_OPAQUE(LocalVec2); PYBIND11_MAKE_OPAQUE(LocalVec2)
PYBIND11_MAKE_OPAQUE(LocalMap); PYBIND11_MAKE_OPAQUE(LocalMap)
PYBIND11_MAKE_OPAQUE(NonLocalVec); PYBIND11_MAKE_OPAQUE(NonLocalVec)
// PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2 // PYBIND11_MAKE_OPAQUE(NonLocalVec2) // same type as LocalVec2
PYBIND11_MAKE_OPAQUE(NonLocalMap); PYBIND11_MAKE_OPAQUE(NonLocalMap)
PYBIND11_MAKE_OPAQUE(NonLocalMap2); PYBIND11_MAKE_OPAQUE(NonLocalMap2)
// Simple bindings (used with the above): // Simple bindings (used with the above):
template <typename T, int Adjust = 0, typename... Args> template <typename T, int Adjust = 0, typename... Args>
@ -70,7 +70,7 @@ py::class_<T> bind_local(Args &&...args) {
return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) { return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) {
return i.i + Adjust; return i.i + Adjust;
}); });
}; }
// Simulate a foreign library base class (to match the example in the docs): // Simulate a foreign library base class (to match the example in the docs):
namespace pets { namespace pets {

View File

@ -0,0 +1,51 @@
// Copyright (c) 2020-2024 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.
#pragma once
#include "pybind11/detail/struct_smart_holder.h"
namespace pybindit {
namespace memory {
namespace smart_holder_poc { // Proof-of-Concept implementations.
template <typename T>
T &as_lvalue_ref(const smart_holder &hld) {
static const char *context = "as_lvalue_ref";
hld.ensure_is_populated(context);
hld.ensure_has_pointee(context);
return *hld.as_raw_ptr_unowned<T>();
}
template <typename T>
T &&as_rvalue_ref(const smart_holder &hld) {
static const char *context = "as_rvalue_ref";
hld.ensure_is_populated(context);
hld.ensure_has_pointee(context);
return std::move(*hld.as_raw_ptr_unowned<T>());
}
template <typename T>
T *as_raw_ptr_release_ownership(smart_holder &hld,
const char *context = "as_raw_ptr_release_ownership") {
hld.ensure_can_release_ownership(context);
T *raw_ptr = hld.as_raw_ptr_unowned<T>();
hld.release_ownership();
return raw_ptr;
}
template <typename T, typename D = std::default_delete<T>>
std::unique_ptr<T, D> as_unique_ptr(smart_holder &hld) {
static const char *context = "as_unique_ptr";
hld.ensure_compatible_rtti_uqp_del<T, D>(context);
hld.ensure_use_count_1(context);
T *raw_ptr = hld.as_raw_ptr_unowned<T>();
hld.release_ownership();
// KNOWN DEFECT (see PR #4850): Does not copy the deleter.
return std::unique_ptr<T, D>(raw_ptr);
}
} // namespace smart_holder_poc
} // namespace memory
} // namespace pybindit

View File

@ -1,6 +1,4 @@
#define PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP #include "smart_holder_poc.h"
#include "pybind11/detail/smart_holder_poc.h"
#include <functional> #include <functional>
#include <memory> #include <memory>
@ -16,6 +14,7 @@
#include "catch.hpp" #include "catch.hpp"
using pybindit::memory::smart_holder; using pybindit::memory::smart_holder;
namespace poc = pybindit::memory::smart_holder_poc;
namespace helpers { namespace helpers {
@ -70,14 +69,14 @@ TEST_CASE("from_raw_ptr_unowned+as_raw_ptr_unowned", "[S]") {
TEST_CASE("from_raw_ptr_unowned+as_lvalue_ref", "[S]") { TEST_CASE("from_raw_ptr_unowned+as_lvalue_ref", "[S]") {
static int value = 19; static int value = 19;
auto hld = smart_holder::from_raw_ptr_unowned(&value); auto hld = smart_holder::from_raw_ptr_unowned(&value);
REQUIRE(hld.as_lvalue_ref<int>() == 19); REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
} }
TEST_CASE("from_raw_ptr_unowned+as_rvalue_ref", "[S]") { TEST_CASE("from_raw_ptr_unowned+as_rvalue_ref", "[S]") {
helpers::movable_int orig(19); helpers::movable_int orig(19);
{ {
auto hld = smart_holder::from_raw_ptr_unowned(&orig); auto hld = smart_holder::from_raw_ptr_unowned(&orig);
helpers::movable_int othr(hld.as_rvalue_ref<helpers::movable_int>()); helpers::movable_int othr(poc::as_rvalue_ref<helpers::movable_int>(hld));
REQUIRE(othr.valu == 19); REQUIRE(othr.valu == 19);
REQUIRE(orig.valu == 91); REQUIRE(orig.valu == 91);
} }
@ -86,21 +85,21 @@ TEST_CASE("from_raw_ptr_unowned+as_rvalue_ref", "[S]") {
TEST_CASE("from_raw_ptr_unowned+as_raw_ptr_release_ownership", "[E]") { TEST_CASE("from_raw_ptr_unowned+as_raw_ptr_release_ownership", "[E]") {
static int value = 19; static int value = 19;
auto hld = smart_holder::from_raw_ptr_unowned(&value); auto hld = smart_holder::from_raw_ptr_unowned(&value);
REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership<int>(), REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership<int>(hld),
"Cannot disown non-owning holder (as_raw_ptr_release_ownership)."); "Cannot disown non-owning holder (as_raw_ptr_release_ownership).");
} }
TEST_CASE("from_raw_ptr_unowned+as_unique_ptr", "[E]") { TEST_CASE("from_raw_ptr_unowned+as_unique_ptr", "[E]") {
static int value = 19; static int value = 19;
auto hld = smart_holder::from_raw_ptr_unowned(&value); auto hld = smart_holder::from_raw_ptr_unowned(&value);
REQUIRE_THROWS_WITH(hld.as_unique_ptr<int>(), REQUIRE_THROWS_WITH(poc::as_unique_ptr<int>(hld),
"Cannot disown non-owning holder (as_unique_ptr)."); "Cannot disown non-owning holder (as_unique_ptr).");
} }
TEST_CASE("from_raw_ptr_unowned+as_unique_ptr_with_deleter", "[E]") { TEST_CASE("from_raw_ptr_unowned+as_unique_ptr_with_deleter", "[E]") {
static int value = 19; static int value = 19;
auto hld = smart_holder::from_raw_ptr_unowned(&value); auto hld = smart_holder::from_raw_ptr_unowned(&value);
REQUIRE_THROWS_WITH((hld.as_unique_ptr<int, helpers::functor_builtin_delete<int>>()), REQUIRE_THROWS_WITH((poc::as_unique_ptr<int, helpers::functor_builtin_delete<int>>(hld)),
"Missing unique_ptr deleter (as_unique_ptr)."); "Missing unique_ptr deleter (as_unique_ptr).");
} }
@ -113,12 +112,12 @@ TEST_CASE("from_raw_ptr_unowned+as_shared_ptr", "[S]") {
TEST_CASE("from_raw_ptr_take_ownership+as_lvalue_ref", "[S]") { TEST_CASE("from_raw_ptr_take_ownership+as_lvalue_ref", "[S]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
REQUIRE(hld.has_pointee()); REQUIRE(hld.has_pointee());
REQUIRE(hld.as_lvalue_ref<int>() == 19); REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
} }
TEST_CASE("from_raw_ptr_take_ownership+as_raw_ptr_release_ownership1", "[S]") { TEST_CASE("from_raw_ptr_take_ownership+as_raw_ptr_release_ownership1", "[S]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
auto new_owner = std::unique_ptr<int>(hld.as_raw_ptr_release_ownership<int>()); auto new_owner = std::unique_ptr<int>(poc::as_raw_ptr_release_ownership<int>(hld));
REQUIRE(!hld.has_pointee()); REQUIRE(!hld.has_pointee());
REQUIRE(*new_owner == 19); REQUIRE(*new_owner == 19);
} }
@ -126,13 +125,13 @@ TEST_CASE("from_raw_ptr_take_ownership+as_raw_ptr_release_ownership1", "[S]") {
TEST_CASE("from_raw_ptr_take_ownership+as_raw_ptr_release_ownership2", "[E]") { TEST_CASE("from_raw_ptr_take_ownership+as_raw_ptr_release_ownership2", "[E]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
auto shd_ptr = hld.as_shared_ptr<int>(); auto shd_ptr = hld.as_shared_ptr<int>();
REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership<int>(), REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership<int>(hld),
"Cannot disown use_count != 1 (as_raw_ptr_release_ownership)."); "Cannot disown use_count != 1 (as_raw_ptr_release_ownership).");
} }
TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr1", "[S]") { TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr1", "[S]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
std::unique_ptr<int> new_owner = hld.as_unique_ptr<int>(); std::unique_ptr<int> new_owner = poc::as_unique_ptr<int>(hld);
REQUIRE(!hld.has_pointee()); REQUIRE(!hld.has_pointee());
REQUIRE(*new_owner == 19); REQUIRE(*new_owner == 19);
} }
@ -140,12 +139,13 @@ TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr1", "[S]") {
TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr2", "[E]") { TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr2", "[E]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
auto shd_ptr = hld.as_shared_ptr<int>(); auto shd_ptr = hld.as_shared_ptr<int>();
REQUIRE_THROWS_WITH(hld.as_unique_ptr<int>(), "Cannot disown use_count != 1 (as_unique_ptr)."); REQUIRE_THROWS_WITH(poc::as_unique_ptr<int>(hld),
"Cannot disown use_count != 1 (as_unique_ptr).");
} }
TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr_with_deleter", "[E]") { TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr_with_deleter", "[E]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
REQUIRE_THROWS_WITH((hld.as_unique_ptr<int, helpers::functor_builtin_delete<int>>()), REQUIRE_THROWS_WITH((poc::as_unique_ptr<int, helpers::functor_builtin_delete<int>>(hld)),
"Missing unique_ptr deleter (as_unique_ptr)."); "Missing unique_ptr deleter (as_unique_ptr).");
} }
@ -160,14 +160,14 @@ TEST_CASE("from_raw_ptr_take_ownership+disown+reclaim_disowned", "[S]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
std::unique_ptr<int> new_owner(hld.as_raw_ptr_unowned<int>()); std::unique_ptr<int> new_owner(hld.as_raw_ptr_unowned<int>());
hld.disown(); hld.disown();
REQUIRE(hld.as_lvalue_ref<int>() == 19); REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
REQUIRE(*new_owner == 19); REQUIRE(*new_owner == 19);
hld.reclaim_disowned(); // Manually veriified: without this, clang++ -fsanitize=address reports hld.reclaim_disowned(); // Manually veriified: without this, clang++ -fsanitize=address reports
// "detected memory leaks". // "detected memory leaks".
// NOLINTNEXTLINE(bugprone-unused-return-value) // NOLINTNEXTLINE(bugprone-unused-return-value)
(void) new_owner.release(); // Manually verified: without this, clang++ -fsanitize=address (void) new_owner.release(); // Manually verified: without this, clang++ -fsanitize=address
// reports "attempting double-free". // reports "attempting double-free".
REQUIRE(hld.as_lvalue_ref<int>() == 19); REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
REQUIRE(new_owner.get() == nullptr); REQUIRE(new_owner.get() == nullptr);
} }
@ -175,7 +175,7 @@ TEST_CASE("from_raw_ptr_take_ownership+disown+release_disowned", "[S]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
std::unique_ptr<int> new_owner(hld.as_raw_ptr_unowned<int>()); std::unique_ptr<int> new_owner(hld.as_raw_ptr_unowned<int>());
hld.disown(); hld.disown();
REQUIRE(hld.as_lvalue_ref<int>() == 19); REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
REQUIRE(*new_owner == 19); REQUIRE(*new_owner == 19);
hld.release_disowned(); hld.release_disowned();
REQUIRE(!hld.has_pointee()); REQUIRE(!hld.has_pointee());
@ -195,14 +195,14 @@ TEST_CASE("from_unique_ptr+as_lvalue_ref", "[S]") {
std::unique_ptr<int> orig_owner(new int(19)); std::unique_ptr<int> orig_owner(new int(19));
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
REQUIRE(hld.as_lvalue_ref<int>() == 19); REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
} }
TEST_CASE("from_unique_ptr+as_raw_ptr_release_ownership1", "[S]") { TEST_CASE("from_unique_ptr+as_raw_ptr_release_ownership1", "[S]") {
std::unique_ptr<int> orig_owner(new int(19)); std::unique_ptr<int> orig_owner(new int(19));
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
auto new_owner = std::unique_ptr<int>(hld.as_raw_ptr_release_ownership<int>()); auto new_owner = std::unique_ptr<int>(poc::as_raw_ptr_release_ownership<int>(hld));
REQUIRE(!hld.has_pointee()); REQUIRE(!hld.has_pointee());
REQUIRE(*new_owner == 19); REQUIRE(*new_owner == 19);
} }
@ -212,7 +212,7 @@ TEST_CASE("from_unique_ptr+as_raw_ptr_release_ownership2", "[E]") {
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
auto shd_ptr = hld.as_shared_ptr<int>(); auto shd_ptr = hld.as_shared_ptr<int>();
REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership<int>(), REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership<int>(hld),
"Cannot disown use_count != 1 (as_raw_ptr_release_ownership)."); "Cannot disown use_count != 1 (as_raw_ptr_release_ownership).");
} }
@ -220,7 +220,7 @@ TEST_CASE("from_unique_ptr+as_unique_ptr1", "[S]") {
std::unique_ptr<int> orig_owner(new int(19)); std::unique_ptr<int> orig_owner(new int(19));
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
std::unique_ptr<int> new_owner = hld.as_unique_ptr<int>(); std::unique_ptr<int> new_owner = poc::as_unique_ptr<int>(hld);
REQUIRE(!hld.has_pointee()); REQUIRE(!hld.has_pointee());
REQUIRE(*new_owner == 19); REQUIRE(*new_owner == 19);
} }
@ -230,14 +230,15 @@ TEST_CASE("from_unique_ptr+as_unique_ptr2", "[E]") {
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
auto shd_ptr = hld.as_shared_ptr<int>(); auto shd_ptr = hld.as_shared_ptr<int>();
REQUIRE_THROWS_WITH(hld.as_unique_ptr<int>(), "Cannot disown use_count != 1 (as_unique_ptr)."); REQUIRE_THROWS_WITH(poc::as_unique_ptr<int>(hld),
"Cannot disown use_count != 1 (as_unique_ptr).");
} }
TEST_CASE("from_unique_ptr+as_unique_ptr_with_deleter", "[E]") { TEST_CASE("from_unique_ptr+as_unique_ptr_with_deleter", "[E]") {
std::unique_ptr<int> orig_owner(new int(19)); std::unique_ptr<int> orig_owner(new int(19));
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
REQUIRE_THROWS_WITH((hld.as_unique_ptr<int, helpers::functor_builtin_delete<int>>()), REQUIRE_THROWS_WITH((poc::as_unique_ptr<int, helpers::functor_builtin_delete<int>>(hld)),
"Incompatible unique_ptr deleter (as_unique_ptr)."); "Incompatible unique_ptr deleter (as_unique_ptr).");
} }
@ -254,7 +255,7 @@ TEST_CASE("from_unique_ptr_derived+as_unique_ptr_base", "[S]") {
std::unique_ptr<helpers::derived> orig_owner(new helpers::derived()); std::unique_ptr<helpers::derived> orig_owner(new helpers::derived());
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
std::unique_ptr<helpers::base> new_owner = hld.as_unique_ptr<helpers::base>(); std::unique_ptr<helpers::base> new_owner = poc::as_unique_ptr<helpers::base>(hld);
REQUIRE(!hld.has_pointee()); REQUIRE(!hld.has_pointee());
REQUIRE(new_owner->get() == 100); REQUIRE(new_owner->get() == 100);
} }
@ -265,7 +266,7 @@ TEST_CASE("from_unique_ptr_derived+as_unique_ptr_base2", "[E]") {
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
REQUIRE_THROWS_WITH( REQUIRE_THROWS_WITH(
(hld.as_unique_ptr<helpers::base, helpers::functor_builtin_delete<helpers::base>>()), (poc::as_unique_ptr<helpers::base, helpers::functor_builtin_delete<helpers::base>>(hld)),
"Incompatible unique_ptr deleter (as_unique_ptr)."); "Incompatible unique_ptr deleter (as_unique_ptr).");
} }
@ -273,7 +274,7 @@ TEST_CASE("from_unique_ptr_with_deleter+as_lvalue_ref", "[S]") {
std::unique_ptr<int, helpers::functor_builtin_delete<int>> orig_owner(new int(19)); std::unique_ptr<int, helpers::functor_builtin_delete<int>> orig_owner(new int(19));
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
REQUIRE(hld.as_lvalue_ref<int>() == 19); REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
} }
TEST_CASE("from_unique_ptr_with_std_function_deleter+as_lvalue_ref", "[S]") { TEST_CASE("from_unique_ptr_with_std_function_deleter+as_lvalue_ref", "[S]") {
@ -281,14 +282,14 @@ TEST_CASE("from_unique_ptr_with_std_function_deleter+as_lvalue_ref", "[S]") {
new int(19), [](const int *raw_ptr) { delete raw_ptr; }); new int(19), [](const int *raw_ptr) { delete raw_ptr; });
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
REQUIRE(hld.as_lvalue_ref<int>() == 19); REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
} }
TEST_CASE("from_unique_ptr_with_deleter+as_raw_ptr_release_ownership", "[E]") { TEST_CASE("from_unique_ptr_with_deleter+as_raw_ptr_release_ownership", "[E]") {
std::unique_ptr<int, helpers::functor_builtin_delete<int>> orig_owner(new int(19)); std::unique_ptr<int, helpers::functor_builtin_delete<int>> orig_owner(new int(19));
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership<int>(), REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership<int>(hld),
"Cannot disown custom deleter (as_raw_ptr_release_ownership)."); "Cannot disown custom deleter (as_raw_ptr_release_ownership).");
} }
@ -296,7 +297,7 @@ TEST_CASE("from_unique_ptr_with_deleter+as_unique_ptr", "[E]") {
std::unique_ptr<int, helpers::functor_builtin_delete<int>> orig_owner(new int(19)); std::unique_ptr<int, helpers::functor_builtin_delete<int>> orig_owner(new int(19));
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
REQUIRE_THROWS_WITH(hld.as_unique_ptr<int>(), REQUIRE_THROWS_WITH(poc::as_unique_ptr<int>(hld),
"Incompatible unique_ptr deleter (as_unique_ptr)."); "Incompatible unique_ptr deleter (as_unique_ptr).");
} }
@ -305,7 +306,7 @@ TEST_CASE("from_unique_ptr_with_deleter+as_unique_ptr_with_deleter1", "[S]") {
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
std::unique_ptr<int, helpers::functor_builtin_delete<int>> new_owner std::unique_ptr<int, helpers::functor_builtin_delete<int>> new_owner
= hld.as_unique_ptr<int, helpers::functor_builtin_delete<int>>(); = poc::as_unique_ptr<int, helpers::functor_builtin_delete<int>>(hld);
REQUIRE(!hld.has_pointee()); REQUIRE(!hld.has_pointee());
REQUIRE(*new_owner == 19); REQUIRE(*new_owner == 19);
} }
@ -314,7 +315,7 @@ TEST_CASE("from_unique_ptr_with_deleter+as_unique_ptr_with_deleter2", "[E]") {
std::unique_ptr<int, helpers::functor_builtin_delete<int>> orig_owner(new int(19)); std::unique_ptr<int, helpers::functor_builtin_delete<int>> orig_owner(new int(19));
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr); REQUIRE(orig_owner.get() == nullptr);
REQUIRE_THROWS_WITH((hld.as_unique_ptr<int, helpers::functor_other_delete<int>>()), REQUIRE_THROWS_WITH((poc::as_unique_ptr<int, helpers::functor_other_delete<int>>(hld)),
"Incompatible unique_ptr deleter (as_unique_ptr)."); "Incompatible unique_ptr deleter (as_unique_ptr).");
} }
@ -330,27 +331,27 @@ TEST_CASE("from_unique_ptr_with_deleter+as_shared_ptr", "[S]") {
TEST_CASE("from_shared_ptr+as_lvalue_ref", "[S]") { TEST_CASE("from_shared_ptr+as_lvalue_ref", "[S]") {
std::shared_ptr<int> orig_owner(new int(19)); std::shared_ptr<int> orig_owner(new int(19));
auto hld = smart_holder::from_shared_ptr(orig_owner); auto hld = smart_holder::from_shared_ptr(orig_owner);
REQUIRE(hld.as_lvalue_ref<int>() == 19); REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
} }
TEST_CASE("from_shared_ptr+as_raw_ptr_release_ownership", "[E]") { TEST_CASE("from_shared_ptr+as_raw_ptr_release_ownership", "[E]") {
std::shared_ptr<int> orig_owner(new int(19)); std::shared_ptr<int> orig_owner(new int(19));
auto hld = smart_holder::from_shared_ptr(orig_owner); auto hld = smart_holder::from_shared_ptr(orig_owner);
REQUIRE_THROWS_WITH(hld.as_raw_ptr_release_ownership<int>(), REQUIRE_THROWS_WITH(poc::as_raw_ptr_release_ownership<int>(hld),
"Cannot disown external shared_ptr (as_raw_ptr_release_ownership)."); "Cannot disown external shared_ptr (as_raw_ptr_release_ownership).");
} }
TEST_CASE("from_shared_ptr+as_unique_ptr", "[E]") { TEST_CASE("from_shared_ptr+as_unique_ptr", "[E]") {
std::shared_ptr<int> orig_owner(new int(19)); std::shared_ptr<int> orig_owner(new int(19));
auto hld = smart_holder::from_shared_ptr(orig_owner); auto hld = smart_holder::from_shared_ptr(orig_owner);
REQUIRE_THROWS_WITH(hld.as_unique_ptr<int>(), REQUIRE_THROWS_WITH(poc::as_unique_ptr<int>(hld),
"Cannot disown external shared_ptr (as_unique_ptr)."); "Cannot disown external shared_ptr (as_unique_ptr).");
} }
TEST_CASE("from_shared_ptr+as_unique_ptr_with_deleter", "[E]") { TEST_CASE("from_shared_ptr+as_unique_ptr_with_deleter", "[E]") {
std::shared_ptr<int> orig_owner(new int(19)); std::shared_ptr<int> orig_owner(new int(19));
auto hld = smart_holder::from_shared_ptr(orig_owner); auto hld = smart_holder::from_shared_ptr(orig_owner);
REQUIRE_THROWS_WITH((hld.as_unique_ptr<int, helpers::functor_builtin_delete<int>>()), REQUIRE_THROWS_WITH((poc::as_unique_ptr<int, helpers::functor_builtin_delete<int>>(hld)),
"Missing unique_ptr deleter (as_unique_ptr)."); "Missing unique_ptr deleter (as_unique_ptr).");
} }
@ -362,19 +363,19 @@ TEST_CASE("from_shared_ptr+as_shared_ptr", "[S]") {
TEST_CASE("error_unpopulated_holder", "[E]") { TEST_CASE("error_unpopulated_holder", "[E]") {
smart_holder hld; smart_holder hld;
REQUIRE_THROWS_WITH(hld.as_lvalue_ref<int>(), "Unpopulated holder (as_lvalue_ref)."); REQUIRE_THROWS_WITH(poc::as_lvalue_ref<int>(hld), "Unpopulated holder (as_lvalue_ref).");
} }
TEST_CASE("error_disowned_holder", "[E]") { TEST_CASE("error_disowned_holder", "[E]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
hld.as_unique_ptr<int>(); poc::as_unique_ptr<int>(hld);
REQUIRE_THROWS_WITH(hld.as_lvalue_ref<int>(), "Disowned holder (as_lvalue_ref)."); REQUIRE_THROWS_WITH(poc::as_lvalue_ref<int>(hld), "Disowned holder (as_lvalue_ref).");
} }
TEST_CASE("error_cannot_disown_nullptr", "[E]") { TEST_CASE("error_cannot_disown_nullptr", "[E]") {
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
hld.as_unique_ptr<int>(); poc::as_unique_ptr<int>(hld);
REQUIRE_THROWS_WITH(hld.as_unique_ptr<int>(), "Cannot disown nullptr (as_unique_ptr)."); REQUIRE_THROWS_WITH(poc::as_unique_ptr<int>(hld), "Cannot disown nullptr (as_unique_ptr).");
} }
TEST_CASE("indestructible_int-from_raw_ptr_unowned+as_raw_ptr_unowned", "[S]") { TEST_CASE("indestructible_int-from_raw_ptr_unowned+as_raw_ptr_unowned", "[S]") {

View File

@ -148,7 +148,7 @@ TEST_SUBMODULE(callbacks, m) {
m.def("dummy_function2", [](int i, int j) { return i + j; }); m.def("dummy_function2", [](int i, int j) { return i + j; });
m.def( m.def(
"roundtrip", "roundtrip",
[](std::function<int(int)> f, bool expect_none = false) { [](std::function<int(int)> f, bool expect_none) {
if (expect_none && f) { if (expect_none && f) {
throw std::runtime_error("Expected None to be converted to empty std::function"); throw std::runtime_error("Expected None to be converted to empty std::function");
} }

View File

@ -120,6 +120,17 @@ std::string get_mtxt(atyp const &obj) { return obj.mtxt; }
std::ptrdiff_t get_ptr(atyp const &obj) { return reinterpret_cast<std::ptrdiff_t>(&obj); } std::ptrdiff_t get_ptr(atyp const &obj) { return reinterpret_cast<std::ptrdiff_t>(&obj); }
std::unique_ptr<atyp> unique_ptr_roundtrip(std::unique_ptr<atyp> obj) { return obj; } std::unique_ptr<atyp> unique_ptr_roundtrip(std::unique_ptr<atyp> obj) { return obj; }
std::string pass_unique_ptr_cref(const std::unique_ptr<atyp> &obj) { return obj->mtxt; }
const std::unique_ptr<atyp> &rtrn_unique_ptr_cref(const std::string &mtxt) {
static std::unique_ptr<atyp> obj{new atyp{"static_ctor_arg"}};
if (!mtxt.empty()) {
obj->mtxt = mtxt;
}
return obj;
}
const std::unique_ptr<atyp> &unique_ptr_cref_roundtrip(const std::unique_ptr<atyp> &obj) { const std::unique_ptr<atyp> &unique_ptr_cref_roundtrip(const std::unique_ptr<atyp> &obj) {
return obj; return obj;
} }
@ -217,6 +228,9 @@ TEST_SUBMODULE(class_sh_basic, m) {
m.def("get_ptr", get_ptr); // pass_cref m.def("get_ptr", get_ptr); // pass_cref
m.def("unique_ptr_roundtrip", unique_ptr_roundtrip); // pass_uqmp, rtrn_uqmp m.def("unique_ptr_roundtrip", unique_ptr_roundtrip); // pass_uqmp, rtrn_uqmp
m.def("pass_unique_ptr_cref", pass_unique_ptr_cref);
m.def("rtrn_unique_ptr_cref", rtrn_unique_ptr_cref);
m.def("unique_ptr_cref_roundtrip", unique_ptr_cref_roundtrip); m.def("unique_ptr_cref_roundtrip", unique_ptr_cref_roundtrip);
py::classh<SharedPtrStash>(m, "SharedPtrStash") py::classh<SharedPtrStash>(m, "SharedPtrStash")

View File

@ -151,19 +151,31 @@ def test_unique_ptr_roundtrip(num_round_trips=1000):
id_orig = id_rtrn id_orig = id_rtrn
# This currently fails, because a unique_ptr is always loaded by value def test_pass_unique_ptr_cref():
# due to pybind11/detail/smart_holder_type_casters.h:689 obj = m.atyp("ctor_arg")
# I think, we need to provide more cast operators. assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.get_mtxt(obj))
@pytest.mark.skip() assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.pass_unique_ptr_cref(obj))
def test_unique_ptr_cref_roundtrip(): assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.get_mtxt(obj))
orig = m.atyp("passenger")
id_orig = id(orig)
mtxt_orig = m.get_mtxt(orig)
recycled = m.unique_ptr_cref_roundtrip(orig)
assert m.get_mtxt(orig) == mtxt_orig def test_rtrn_unique_ptr_cref():
assert m.get_mtxt(recycled) == mtxt_orig obj0 = m.rtrn_unique_ptr_cref("")
assert id(recycled) == id_orig assert m.get_mtxt(obj0) == "static_ctor_arg"
obj1 = m.rtrn_unique_ptr_cref("passed_mtxt_1")
assert m.get_mtxt(obj1) == "passed_mtxt_1"
assert m.get_mtxt(obj0) == "passed_mtxt_1"
assert obj0 is obj1
def test_unique_ptr_cref_roundtrip(num_round_trips=1000):
# Multiple roundtrips to stress-test implementation.
orig = m.atyp("passenger")
mtxt_orig = m.get_mtxt(orig)
recycled = orig
for _ in range(num_round_trips):
recycled = m.unique_ptr_cref_roundtrip(recycled)
assert recycled is orig
assert m.get_mtxt(recycled) == mtxt_orig
@pytest.mark.parametrize( @pytest.mark.parametrize(

View File

@ -1,30 +0,0 @@
from __future__ import annotations
import class_sh_module_local_0 as m0
import class_sh_module_local_1 as m1
import class_sh_module_local_2 as m2
import pytest
if not m0.defined_PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
pytest.skip("smart_holder not available.", allow_module_level=True)
def test_cross_module_get_mtxt():
obj1 = m1.atyp("A")
assert obj1.tag() == 1
obj2 = m2.atyp("B")
assert obj2.tag() == 2
assert m1.get_mtxt(obj1) == "A"
assert m2.get_mtxt(obj2) == "B"
assert m1.get_mtxt(obj2) == "B"
assert m2.get_mtxt(obj1) == "A"
assert m0.get_mtxt(obj1) == "A"
assert m0.get_mtxt(obj2) == "B"
def test_m0_rtrn_valu_atyp():
with pytest.raises(TypeError) as exc_info:
m0.rtrn_valu_atyp()
assert str(exc_info.value).startswith(
"Unable to convert function return value to a Python type!"
)

View File

@ -87,7 +87,12 @@ long pass_shared_ptr(const std::shared_ptr<Sft> &obj) {
return sft.use_count(); return sft.use_count();
} }
void pass_unique_ptr(const std::unique_ptr<Sft> &) {} std::string pass_unique_ptr_cref(const std::unique_ptr<Sft> &obj) {
return obj ? obj->history : "<NULLPTR>";
}
void pass_unique_ptr_rref(std::unique_ptr<Sft> &&) {
throw std::runtime_error("Expected to not be reached.");
}
Sft *make_pure_cpp_sft_raw_ptr(const std::string &history_seed) { return new Sft{history_seed}; } Sft *make_pure_cpp_sft_raw_ptr(const std::string &history_seed) { return new Sft{history_seed}; }
@ -135,7 +140,8 @@ TEST_SUBMODULE(class_sh_trampoline_shared_from_this, m) {
m.def("use_count", use_count); m.def("use_count", use_count);
m.def("pass_shared_ptr", pass_shared_ptr); m.def("pass_shared_ptr", pass_shared_ptr);
m.def("pass_unique_ptr", pass_unique_ptr); m.def("pass_unique_ptr_cref", pass_unique_ptr_cref);
m.def("pass_unique_ptr_rref", pass_unique_ptr_rref);
m.def("make_pure_cpp_sft_raw_ptr", make_pure_cpp_sft_raw_ptr); m.def("make_pure_cpp_sft_raw_ptr", make_pure_cpp_sft_raw_ptr);
m.def("make_pure_cpp_sft_unq_ptr", make_pure_cpp_sft_unq_ptr); m.def("make_pure_cpp_sft_unq_ptr", make_pure_cpp_sft_unq_ptr);
m.def("make_pure_cpp_sft_shd_ptr", make_pure_cpp_sft_shd_ptr); m.def("make_pure_cpp_sft_shd_ptr", make_pure_cpp_sft_shd_ptr);

View File

@ -137,11 +137,14 @@ def test_pass_released_shared_ptr_as_unique_ptr():
obj = PySft("PySft") obj = PySft("PySft")
stash1 = m.SftSharedPtrStash(1) stash1 = m.SftSharedPtrStash(1)
stash1.Add(obj) # Releases shared_ptr to C++. stash1.Add(obj) # Releases shared_ptr to C++.
assert m.pass_unique_ptr_cref(obj) == "PySft_Stash1Add"
assert obj.history == "PySft_Stash1Add"
with pytest.raises(ValueError) as exc_info: with pytest.raises(ValueError) as exc_info:
m.pass_unique_ptr(obj) m.pass_unique_ptr_rref(obj)
assert str(exc_info.value) == ( assert str(exc_info.value) == (
"Python instance is currently owned by a std::shared_ptr." "Python instance is currently owned by a std::shared_ptr."
) )
assert obj.history == "PySft_Stash1Add"
@pytest.mark.parametrize( @pytest.mark.parametrize(

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_installed_embed CXX) project(test_installed_embed CXX)

View File

@ -1,14 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
project(test_installed_module CXX)
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_installed_function CXX) project(test_installed_function CXX)

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_installed_target CXX) project(test_installed_target CXX)

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_subdirectory_embed CXX) project(test_subdirectory_embed CXX)

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_subdirectory_function CXX) project(test_subdirectory_function CXX)

View File

@ -1,13 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# The `cmake_minimum_required(VERSION 3.5...3.29)` 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.29)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.29)
endif()
project(test_subdirectory_target CXX) project(test_subdirectory_target CXX)

View File

@ -55,7 +55,7 @@ void reset_refs() {
} }
// Returns element 2,1 from a matrix (used to test copy/nocopy) // Returns element 2,1 from a matrix (used to test copy/nocopy)
double get_elem(const Eigen::Ref<const Eigen::MatrixXd> &m) { return m(2, 1); }; double get_elem(const Eigen::Ref<const Eigen::MatrixXd> &m) { return m(2, 1); }
// Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix // Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix
// reference is referencing rows/columns correctly). // reference is referencing rows/columns correctly).
@ -76,7 +76,7 @@ struct CustomOperatorNew {
Eigen::Matrix4d a = Eigen::Matrix4d::Zero(); Eigen::Matrix4d a = Eigen::Matrix4d::Zero();
Eigen::Matrix4d b = Eigen::Matrix4d::Identity(); Eigen::Matrix4d b = Eigen::Matrix4d::Identity();
EIGEN_MAKE_ALIGNED_OPERATOR_NEW; EIGEN_MAKE_ALIGNED_OPERATOR_NEW
}; };
TEST_SUBMODULE(eigen_matrix, m) { TEST_SUBMODULE(eigen_matrix, m) {

View File

@ -18,7 +18,7 @@
// This also deliberately doesn't use the below StringList type alias to test // This also deliberately doesn't use the below StringList type alias to test
// that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator` // that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator`
// bit is just the default `std::vector` allocator). // bit is just the default `std::vector` allocator).
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>); PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>)
using StringList = std::vector<std::string, std::allocator<std::string>>; using StringList = std::vector<std::string, std::allocator<std::string>>;

View File

@ -13,6 +13,14 @@
#include <utility> #include <utility>
//__has_include has been part of C++17, no need to check it
#if defined(PYBIND11_CPP20) && __has_include(<ranges>)
# if !defined(PYBIND11_COMPILER_CLANG) || __clang_major__ >= 16 // llvm/llvm-project#52696
# define PYBIND11_TEST_PYTYPES_HAS_RANGES
# include <ranges>
# endif
#endif
namespace external { namespace external {
namespace detail { namespace detail {
bool check(PyObject *o) { return PyFloat_Check(o) != 0; } bool check(PyObject *o) { return PyFloat_Check(o) != 0; }
@ -923,4 +931,59 @@ TEST_SUBMODULE(pytypes, m) {
#else #else
m.attr("defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL") = false; m.attr("defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL") = false;
#endif #endif
#if defined(PYBIND11_TEST_PYTYPES_HAS_RANGES)
// test_tuple_ranges
m.def("tuple_iterator_default_initialization", []() {
using TupleIterator = decltype(std::declval<py::tuple>().begin());
static_assert(std::random_access_iterator<TupleIterator>);
return TupleIterator{} == TupleIterator{};
});
m.def("transform_tuple_plus_one", [](py::tuple &tpl) {
py::list ret{};
for (auto it : tpl | std::views::transform([](auto &o) { return py::cast<int>(o) + 1; })) {
ret.append(py::int_(it));
}
return ret;
});
// test_list_ranges
m.def("list_iterator_default_initialization", []() {
using ListIterator = decltype(std::declval<py::list>().begin());
static_assert(std::random_access_iterator<ListIterator>);
return ListIterator{} == ListIterator{};
});
m.def("transform_list_plus_one", [](py::list &lst) {
py::list ret{};
for (auto it : lst | std::views::transform([](auto &o) { return py::cast<int>(o) + 1; })) {
ret.append(py::int_(it));
}
return ret;
});
// test_dict_ranges
m.def("dict_iterator_default_initialization", []() {
using DictIterator = decltype(std::declval<py::dict>().begin());
static_assert(std::forward_iterator<DictIterator>);
return DictIterator{} == DictIterator{};
});
m.def("transform_dict_plus_one", [](py::dict &dct) {
py::list ret{};
for (auto it : dct | std::views::transform([](auto &o) {
return std::pair{py::cast<int>(o.first) + 1,
py::cast<int>(o.second) + 1};
})) {
ret.append(py::make_tuple(py::int_(it.first), py::int_(it.second)));
}
return ret;
});
m.attr("defined_PYBIND11_TEST_PYTYPES_HAS_RANGES") = true;
#else
m.attr("defined_PYBIND11_TEST_PYTYPES_HAS_RANGES") = false;
#endif
} }

View File

@ -1048,3 +1048,45 @@ def test_typevar(doc):
assert doc(m.annotate_listT_to_T) == "annotate_listT_to_T(arg0: list[T]) -> T" assert doc(m.annotate_listT_to_T) == "annotate_listT_to_T(arg0: list[T]) -> T"
assert doc(m.annotate_object_to_T) == "annotate_object_to_T(arg0: object) -> T" assert doc(m.annotate_object_to_T) == "annotate_object_to_T(arg0: object) -> T"
@pytest.mark.skipif(
not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES,
reason="<ranges> not available.",
)
@pytest.mark.parametrize(
("tested_tuple", "expected"),
[((1,), [2]), ((3, 4), [4, 5]), ((7, 8, 9), [8, 9, 10])],
)
def test_tuple_ranges(tested_tuple, expected):
assert m.tuple_iterator_default_initialization()
assert m.transform_tuple_plus_one(tested_tuple) == expected
@pytest.mark.skipif(
not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES,
reason="<ranges> not available.",
)
@pytest.mark.parametrize(
("tested_list", "expected"), [([1], [2]), ([3, 4], [4, 5]), ([7, 8, 9], [8, 9, 10])]
)
def test_list_ranges(tested_list, expected):
assert m.list_iterator_default_initialization()
assert m.transform_list_plus_one(tested_list) == expected
@pytest.mark.skipif(
not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES,
reason="<ranges> not available.",
)
@pytest.mark.parametrize(
("tested_dict", "expected"),
[
({1: 2}, [(2, 3)]),
({3: 4, 5: 6}, [(4, 5), (6, 7)]),
({7: 8, 9: 10, 11: 12}, [(8, 9), (10, 11), (12, 13)]),
],
)
def test_dict_ranges(tested_dict, expected):
assert m.dict_iterator_default_initialization()
assert m.transform_dict_plus_one(tested_dict) == expected

View File

@ -86,8 +86,8 @@ private:
}; };
using NonCopyableIntPair = std::pair<NonCopyableInt, NonCopyableInt>; using NonCopyableIntPair = std::pair<NonCopyableInt, NonCopyableInt>;
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>); PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>)
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>); PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>)
template <typename PythonType> template <typename PythonType>
py::list test_random_access_iterator(PythonType x) { py::list test_random_access_iterator(PythonType x) {

View File

@ -11,6 +11,9 @@
#include "object.h" #include "object.h"
#include "pybind11_tests.h" #include "pybind11_tests.h"
// This breaks on PYBIND11_DECLARE_HOLDER_TYPE
PYBIND11_WARNING_DISABLE_GCC("-Wpedantic")
namespace { namespace {
// This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the // This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the
@ -279,13 +282,13 @@ struct holder_helper<ref<T>> {
} // namespace PYBIND11_NAMESPACE } // namespace PYBIND11_NAMESPACE
// Make pybind aware of the ref-counted wrapper type (s): // Make pybind aware of the ref-counted wrapper type (s):
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true); PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true)
// The following is not required anymore for std::shared_ptr, but it should compile without error: // The following is not required anymore for std::shared_ptr, but it should compile without error:
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr<T>)
PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>)
PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator<T>)
PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>)
TEST_SUBMODULE(smart_ptr, m) { TEST_SUBMODULE(smart_ptr, m) {
// Please do not interleave `struct` and `class` definitions with bindings code, // Please do not interleave `struct` and `class` definitions with bindings code,

View File

@ -59,7 +59,7 @@ struct visit_helper<boost::variant> {
} // namespace PYBIND11_NAMESPACE } // namespace PYBIND11_NAMESPACE
#endif #endif
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>); PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>)
/// Issue #528: templated constructor /// Issue #528: templated constructor
struct TplCtorClass { struct TplCtorClass {
@ -167,6 +167,14 @@ struct type_caster<ReferenceSensitiveOptional<T>>
} // namespace detail } // namespace detail
} // namespace PYBIND11_NAMESPACE } // namespace PYBIND11_NAMESPACE
int pass_std_vector_int(const std::vector<int> &v) {
int zum = 100;
for (const int i : v) {
zum += 2 * i;
}
return zum;
}
TEST_SUBMODULE(stl, m) { TEST_SUBMODULE(stl, m) {
// test_vector // test_vector
m.def("cast_vector", []() { return std::vector<int>{1}; }); m.def("cast_vector", []() { return std::vector<int>{1}; });
@ -193,6 +201,23 @@ TEST_SUBMODULE(stl, m) {
m.def("cast_array", []() { return std::array<int, 2>{{1, 2}}; }); m.def("cast_array", []() { return std::array<int, 2>{{1, 2}}; });
m.def("load_array", [](const std::array<int, 2> &a) { return a[0] == 1 && a[1] == 2; }); m.def("load_array", [](const std::array<int, 2> &a) { return a[0] == 1 && a[1] == 2; });
struct NoDefaultCtor {
explicit constexpr NoDefaultCtor(int val) : val{val} {}
int val;
};
struct NoDefaultCtorArray {
explicit constexpr NoDefaultCtorArray(int i)
: arr{{NoDefaultCtor(10 + i), NoDefaultCtor(20 + i)}} {}
std::array<NoDefaultCtor, 2> arr;
};
// test_array_no_default_ctor
py::class_<NoDefaultCtor>(m, "NoDefaultCtor").def_readonly("val", &NoDefaultCtor::val);
py::class_<NoDefaultCtorArray>(m, "NoDefaultCtorArray")
.def(py::init<int>())
.def_readwrite("arr", &NoDefaultCtorArray::arr);
// test_valarray // test_valarray
m.def("cast_valarray", []() { return std::valarray<int>{1, 4, 9}; }); m.def("cast_valarray", []() { return std::valarray<int>{1, 4, 9}; });
m.def("load_valarray", [](const std::valarray<int> &v) { m.def("load_valarray", [](const std::valarray<int> &v) {
@ -546,4 +571,30 @@ TEST_SUBMODULE(stl, m) {
[]() { return new std::vector<bool>(4513); }, []() { return new std::vector<bool>(4513); },
// Without explicitly specifying `take_ownership`, this function leaks. // Without explicitly specifying `take_ownership`, this function leaks.
py::return_value_policy::take_ownership); py::return_value_policy::take_ownership);
m.def("pass_std_vector_int", pass_std_vector_int);
m.def("pass_std_vector_pair_int", [](const std::vector<std::pair<int, int>> &v) {
int zum = 0;
for (const auto &ij : v) {
zum += ij.first * 100 + ij.second;
}
return zum;
});
m.def("pass_std_array_int_2", [](const std::array<int, 2> &a) {
return pass_std_vector_int(std::vector<int>(a.begin(), a.end())) + 1;
});
m.def("pass_std_set_int", [](const std::set<int> &s) {
int zum = 200;
for (const int i : s) {
zum += 3 * i;
}
return zum;
});
m.def("pass_std_map_int", [](const std::map<int, int> &m) {
int zum = 500;
for (const auto &p : m) {
zum += p.first * 1000 + p.second;
}
return zum;
});
} }

View File

@ -48,6 +48,13 @@ def test_array(doc):
) )
def test_array_no_default_ctor():
lst = m.NoDefaultCtorArray(3)
assert [e.val for e in lst.arr] == [13, 23]
lst.arr = m.NoDefaultCtorArray(4).arr
assert [e.val for e in lst.arr] == [14, 24]
def test_valarray(doc): def test_valarray(doc):
"""std::valarray <-> list""" """std::valarray <-> list"""
lst = m.cast_valarray() lst = m.cast_valarray()
@ -381,3 +388,129 @@ def test_return_vector_bool_raw_ptr():
v = m.return_vector_bool_raw_ptr() v = m.return_vector_bool_raw_ptr()
assert isinstance(v, list) assert isinstance(v, list)
assert len(v) == 4513 assert len(v) == 4513
@pytest.mark.parametrize(
("fn", "offset"), [(m.pass_std_vector_int, 0), (m.pass_std_array_int_2, 1)]
)
def test_pass_std_vector_int(fn, offset):
assert fn([7, 13]) == 140 + offset
assert fn({6, 2}) == 116 + offset
assert fn({"x": 8, "y": 11}.values()) == 138 + offset
assert fn({3: None, 9: None}.keys()) == 124 + offset
assert fn(i for i in [4, 17]) == 142 + offset
assert fn(map(lambda i: i * 3, [8, 7])) == 190 + offset # noqa: C417
with pytest.raises(TypeError):
fn({"x": 0, "y": 1})
with pytest.raises(TypeError):
fn({})
def test_pass_std_vector_pair_int():
fn = m.pass_std_vector_pair_int
assert fn({1: 2, 3: 4}.items()) == 406
assert fn(zip([5, 17], [13, 9])) == 2222
def test_list_caster_fully_consumes_generator_object():
def gen_invalid():
yield from [1, 2.0, 3]
gen_obj = gen_invalid()
with pytest.raises(TypeError):
m.pass_std_vector_int(gen_obj)
assert not tuple(gen_obj)
def test_pass_std_set_int():
fn = m.pass_std_set_int
assert fn({3, 15}) == 254
assert fn({5: None, 12: None}.keys()) == 251
with pytest.raises(TypeError):
fn([])
with pytest.raises(TypeError):
fn({})
with pytest.raises(TypeError):
fn({}.values())
with pytest.raises(TypeError):
fn(i for i in [])
def test_set_caster_dict_keys_failure():
dict_keys = {1: None, 2.0: None, 3: None}.keys()
# The asserts does not really exercise anything in pybind11, but if one of
# them fails in some future version of Python, the set_caster load
# implementation may need to be revisited.
assert tuple(dict_keys) == (1, 2.0, 3)
assert tuple(dict_keys) == (1, 2.0, 3)
with pytest.raises(TypeError):
m.pass_std_set_int(dict_keys)
assert tuple(dict_keys) == (1, 2.0, 3)
class FakePyMappingMissingItems:
def __getitem__(self, _):
raise RuntimeError("Not expected to be called.")
class FakePyMappingWithItems(FakePyMappingMissingItems):
def items(self):
return ((1, 3), (2, 4))
class FakePyMappingBadItems(FakePyMappingMissingItems):
def items(self):
return ((1, 2), (3, "x"))
class FakePyMappingItemsNotCallable(FakePyMappingMissingItems):
@property
def items(self):
return ((1, 2), (3, 4))
class FakePyMappingItemsWithArg(FakePyMappingMissingItems):
def items(self, _):
return ((1, 2), (3, 4))
class FakePyMappingGenObj(FakePyMappingMissingItems):
def __init__(self, gen_obj):
super().__init__()
self.gen_obj = gen_obj
def items(self):
yield from self.gen_obj
def test_pass_std_map_int():
fn = m.pass_std_map_int
assert fn({1: 2, 3: 4}) == 4506
with pytest.raises(TypeError):
fn([])
assert fn(FakePyMappingWithItems()) == 3507
with pytest.raises(TypeError):
fn(FakePyMappingMissingItems())
with pytest.raises(TypeError):
fn(FakePyMappingBadItems())
with pytest.raises(TypeError):
fn(FakePyMappingItemsNotCallable())
with pytest.raises(TypeError):
fn(FakePyMappingItemsWithArg())
@pytest.mark.parametrize(
("items", "expected_exception"),
[
(((1, 2), (3, "x"), (4, 5)), TypeError),
(((1, 2), (3, 4, 5), (6, 7)), ValueError),
],
)
def test_map_caster_fully_consumes_generator_object(items, expected_exception):
def gen_invalid():
yield from items
gen_obj = gen_invalid()
with pytest.raises(expected_exception):
m.pass_std_map_int(FakePyMappingGenObj(gen_obj))
assert not tuple(gen_obj)

View File

@ -145,4 +145,4 @@ TEST_SUBMODULE(tagbased_polymorphic, m) {
.def(py::init<std::string>()) .def(py::init<std::string>())
.def("purr", &Panther::purr); .def("purr", &Panther::purr);
m.def("create_zoo", &create_zoo); m.def("create_zoo", &create_zoo);
}; }

View File

@ -0,0 +1,46 @@
#include <pybind11/functional.h>
#include <pybind11/pybind11.h>
#include "pybind11_tests.h"
namespace py = pybind11;
namespace {
struct SpecialReturn {
int value = 99;
};
} // namespace
namespace pybind11 {
namespace detail {
namespace type_caster_std_function_specializations {
template <typename... Args>
struct func_wrapper<SpecialReturn, Args...> : func_wrapper_base {
using func_wrapper_base::func_wrapper_base;
SpecialReturn operator()(Args... args) const {
gil_scoped_acquire acq;
SpecialReturn result;
try {
result = hfunc.f(std::forward<Args>(args)...).template cast<SpecialReturn>();
} catch (error_already_set &) {
result.value += 1;
}
result.value += 100;
return result;
}
};
} // namespace type_caster_std_function_specializations
} // namespace detail
} // namespace pybind11
TEST_SUBMODULE(type_caster_std_function_specializations, m) {
py::class_<SpecialReturn>(m, "SpecialReturn")
.def(py::init<>())
.def_readwrite("value", &SpecialReturn::value);
m.def("call_callback_with_special_return",
[](const std::function<SpecialReturn()> &func) { return func(); });
}

View File

@ -0,0 +1,15 @@
from __future__ import annotations
from pybind11_tests import type_caster_std_function_specializations as m
def test_callback_with_special_return():
def return_special():
return m.SpecialReturn()
def raise_exception():
raise ValueError("called raise_exception.")
assert return_special().value == 99
assert m.call_callback_with_special_return(return_special).value == 199
assert m.call_callback_with_special_return(raise_exception).value == 200

View File

@ -589,4 +589,4 @@ void initialize_inherited_virtuals(py::module_ &m) {
// Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7) // Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
m.def("test_gil", &test_gil); m.def("test_gil", &test_gil);
m.def("test_gil_from_thread", &test_gil_from_thread); m.def("test_gil_from_thread", &test_gil_from_thread);
}; }

View File

@ -2,7 +2,7 @@
Adds the following targets:: Adds the following targets::
pybind11::pybind11 - link to headers and pybind11 pybind11::pybind11 - link to Python headers and pybind11::headers
pybind11::module - Adds module links pybind11::module - Adds module links
pybind11::embed - Adds embed links pybind11::embed - Adds embed links
pybind11::lto - Link time optimizations (only if CMAKE_INTERPROCEDURAL_OPTIMIZATION is not set) pybind11::lto - Link time optimizations (only if CMAKE_INTERPROCEDURAL_OPTIMIZATION is not set)
@ -18,11 +18,7 @@ Adds the following functions::
#]======================================================] #]======================================================]
# CMake 3.10 has an include_guard command, but we can't use that yet include_guard(GLOBAL)
# include_guard(global) (pre-CMake 3.10)
if(TARGET pybind11::pybind11)
return()
endif()
# If we are in subdirectory mode, all IMPORTED targets must be GLOBAL. If we # If we are in subdirectory mode, all IMPORTED targets must be GLOBAL. If we
# are in CONFIG mode, they should be "normal" targets instead. # are in CONFIG mode, they should be "normal" targets instead.
@ -75,26 +71,36 @@ set_property(
APPEND APPEND
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11) PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11)
# -------------- emscripten requires exceptions enabled -------------
# _pybind11_no_exceptions is a private mechanism to disable this addition.
# Please open an issue if you need to use it; it will be removed if no one
# needs it.
if(CMAKE_SYSTEM_NAME MATCHES Emscripten AND NOT _pybind11_no_exceptions)
if(is_config)
set(_tmp_config_target pybind11::pybind11_headers)
else()
set(_tmp_config_target pybind11_headers)
endif()
set_property(
TARGET ${_tmp_config_target}
APPEND
PROPERTY INTERFACE_LINK_OPTIONS -fexceptions)
set_property(
TARGET ${_tmp_config_target}
APPEND
PROPERTY INTERFACE_COMPILE_OPTIONS -fexceptions)
unset(_tmp_config_target)
endif()
# --------------------------- link helper --------------------------- # --------------------------- link helper ---------------------------
add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global}) add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global})
if(CMAKE_VERSION VERSION_LESS 3.13) set_property(
# In CMake 3.11+, you can set INTERFACE properties via the normal methods, and TARGET pybind11::python_link_helper
# this would be simpler. APPEND
set_property( PROPERTY INTERFACE_LINK_OPTIONS "$<$<PLATFORM_ID:Darwin>:LINKER:-undefined,dynamic_lookup>")
TARGET pybind11::python_link_helper
APPEND
PROPERTY INTERFACE_LINK_LIBRARIES "$<$<PLATFORM_ID:Darwin>:-undefined dynamic_lookup>")
else()
# link_options was added in 3.13+
# This is safer, because you are ensured the deduplication pass in CMake will not consider
# these separate and remove one but not the other.
set_property(
TARGET pybind11::python_link_helper
APPEND
PROPERTY INTERFACE_LINK_OPTIONS "$<$<PLATFORM_ID:Darwin>:LINKER:-undefined,dynamic_lookup>")
endif()
# ------------------------ Windows extras ------------------------- # ------------------------ Windows extras -------------------------
@ -110,22 +116,14 @@ if(MSVC) # That's also clang-cl
# /MP enables multithreaded builds (relevant when there are many files) for MSVC # /MP enables multithreaded builds (relevant when there are many files) for MSVC
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # no Clang no Intel if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # no Clang no Intel
if(CMAKE_VERSION VERSION_LESS 3.11) # Only set these options for C++ files. This is important so that, for
set_property( # instance, projects that include other types of source files like CUDA
TARGET pybind11::windows_extras # .cu files don't get these options propagated to nvcc since that would
APPEND # cause the build to fail.
PROPERTY INTERFACE_COMPILE_OPTIONS $<$<NOT:$<CONFIG:Debug>>:/MP>) set_property(
else() TARGET pybind11::windows_extras
# Only set these options for C++ files. This is important so that, for APPEND
# instance, projects that include other types of source files like CUDA PROPERTY INTERFACE_COMPILE_OPTIONS $<$<NOT:$<CONFIG:Debug>>:$<$<COMPILE_LANGUAGE:CXX>:/MP>>)
# .cu files don't get these options propagated to nvcc since that would
# cause the build to fail.
set_property(
TARGET pybind11::windows_extras
APPEND
PROPERTY INTERFACE_COMPILE_OPTIONS
$<$<NOT:$<CONFIG:Debug>>:$<$<COMPILE_LANGUAGE:CXX>:/MP>>)
endif()
endif() endif()
endif() endif()
@ -329,7 +327,7 @@ function(_pybind11_generate_lto target prefer_thin_lto)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64") if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64")
# Do nothing # Do nothing
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES emscripten) elseif(CMAKE_SYSTEM_NAME MATCHES Emscripten)
# This compile is very costly when cross-compiling, so set this without checking # This compile is very costly when cross-compiling, so set this without checking
set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_append}") set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_append}")
set(PYBIND11_LTO_LINKER_FLAGS "-flto${thin}${linker_append}") set(PYBIND11_LTO_LINKER_FLAGS "-flto${thin}${linker_append}")
@ -371,11 +369,7 @@ function(_pybind11_generate_lto target prefer_thin_lto)
set(is_debug "$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>") set(is_debug "$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>")
set(not_debug "$<NOT:${is_debug}>") set(not_debug "$<NOT:${is_debug}>")
set(cxx_lang "$<COMPILE_LANGUAGE:CXX>") set(cxx_lang "$<COMPILE_LANGUAGE:CXX>")
if(MSVC AND CMAKE_VERSION VERSION_LESS 3.11) set(genex "$<AND:${not_debug},${cxx_lang}>")
set(genex "${not_debug}")
else()
set(genex "$<AND:${not_debug},${cxx_lang}>")
endif()
set_property( set_property(
TARGET ${target} TARGET ${target}
APPEND APPEND
@ -390,17 +384,10 @@ function(_pybind11_generate_lto target prefer_thin_lto)
endif() endif()
if(PYBIND11_LTO_LINKER_FLAGS) if(PYBIND11_LTO_LINKER_FLAGS)
if(CMAKE_VERSION VERSION_LESS 3.11) set_property(
set_property( TARGET ${target}
TARGET ${target} APPEND
APPEND PROPERTY INTERFACE_LINK_OPTIONS "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>")
PROPERTY INTERFACE_LINK_LIBRARIES "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>")
else()
set_property(
TARGET ${target}
APPEND
PROPERTY INTERFACE_LINK_OPTIONS "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>")
endif()
endif() endif()
endfunction() endfunction()

View File

@ -84,7 +84,7 @@ you can either use the basic targets, or use the FindPython tools:
# Python method: # Python method:
Python_add_library(MyModule2 src2.cpp) Python_add_library(MyModule2 src2.cpp)
target_link_libraries(MyModule2 pybind11::headers) target_link_libraries(MyModule2 PUBLIC pybind11::headers)
set_target_properties(MyModule2 PROPERTIES set_target_properties(MyModule2 PROPERTIES
INTERPROCEDURAL_OPTIMIZATION ON INTERPROCEDURAL_OPTIMIZATION ON
CXX_VISIBILITY_PRESET ON CXX_VISIBILITY_PRESET ON

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
function(pybind11_guess_python_module_extension python) function(pybind11_guess_python_module_extension python)

View File

@ -5,10 +5,6 @@
# All rights reserved. Use of this source code is governed by a # All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file. # BSD-style license that can be found in the LICENSE file.
if(CMAKE_VERSION VERSION_LESS 3.12)
message(FATAL_ERROR "You cannot use the new FindPython module with CMake < 3.12")
endif()
include_guard(DIRECTORY) include_guard(DIRECTORY)
get_property( get_property(
@ -236,7 +232,6 @@ if(TARGET ${_Python}::Python)
PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Python) PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Python)
endif() endif()
# CMake 3.15+ has this
if(TARGET ${_Python}::Module) if(TARGET ${_Python}::Module)
set_property( set_property(
TARGET pybind11::module TARGET pybind11::module

View File

@ -5,13 +5,7 @@
# All rights reserved. Use of this source code is governed by a # All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file. # BSD-style license that can be found in the LICENSE file.
# include_guard(global) (pre-CMake 3.10) include_guard(GLOBAL)
if(TARGET pybind11::python_headers)
return()
endif()
# Built-in in CMake 3.5+
include(CMakeParseArguments)
if(pybind11_FIND_QUIETLY) if(pybind11_FIND_QUIETLY)
set(_pybind11_quiet QUIET) set(_pybind11_quiet QUIET)
@ -116,36 +110,19 @@ if(PYTHON_IS_DEBUG)
PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG) PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG)
endif() endif()
# The <3.11 code here does not support release/debug builds at the same time, like on vcpkg # The IMPORTED INTERFACE library here is to ensure that "debug" and "release" get processed outside
if(CMAKE_VERSION VERSION_LESS 3.11) # of a generator expression - https://gitlab.kitware.com/cmake/cmake/-/issues/18424, as they are
set_property( # target_link_library keywords rather than real libraries.
TARGET pybind11::module add_library(pybind11::_ClassicPythonLibraries IMPORTED INTERFACE)
APPEND target_link_libraries(pybind11::_ClassicPythonLibraries INTERFACE ${PYTHON_LIBRARIES})
PROPERTY target_link_libraries(
INTERFACE_LINK_LIBRARIES pybind11::module
pybind11::python_link_helper INTERFACE
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:$<BUILD_INTERFACE:${PYTHON_LIBRARIES}>>" pybind11::python_link_helper
) "$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:pybind11::_ClassicPythonLibraries>")
set_property( target_link_libraries(pybind11::embed INTERFACE pybind11::pybind11
TARGET pybind11::embed pybind11::_ClassicPythonLibraries)
APPEND
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11 $<BUILD_INTERFACE:${PYTHON_LIBRARIES}>)
else()
# The IMPORTED INTERFACE library here is to ensure that "debug" and "release" get processed outside
# of a generator expression - https://gitlab.kitware.com/cmake/cmake/-/issues/18424, as they are
# target_link_library keywords rather than real libraries.
add_library(pybind11::_ClassicPythonLibraries IMPORTED INTERFACE)
target_link_libraries(pybind11::_ClassicPythonLibraries INTERFACE ${PYTHON_LIBRARIES})
target_link_libraries(
pybind11::module
INTERFACE
pybind11::python_link_helper
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:pybind11::_ClassicPythonLibraries>")
target_link_libraries(pybind11::embed INTERFACE pybind11::pybind11
pybind11::_ClassicPythonLibraries)
endif()
function(pybind11_extension name) function(pybind11_extension name)
# The prefix and extension are provided by FindPythonLibsNew.cmake # The prefix and extension are provided by FindPythonLibsNew.cmake

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.15...3.30)
# Tests for pybind11_guess_python_module_extension # Tests for pybind11_guess_python_module_extension
# Run using `cmake -P tools/test-pybind11GuessPythonExtSuffix.cmake` # Run using `cmake -P tools/test-pybind11GuessPythonExtSuffix.cmake`