mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-21 20:55:11 +00:00
Merge branch 'smart_holder' into native_enum_internals_sh
This commit is contained in:
commit
b77283dacf
10
.github/CONTRIBUTING.md
vendored
10
.github/CONTRIBUTING.md
vendored
@ -81,7 +81,7 @@ nox -s build
|
||||
### Full setup
|
||||
|
||||
To setup an ideal development environment, run the following commands on a
|
||||
system with CMake 3.14+:
|
||||
system with CMake 3.15+:
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
@ -96,8 +96,8 @@ Tips:
|
||||
* You can use `virtualenv` (faster, from PyPI) instead of `venv`.
|
||||
* You can select any name for your environment folder; if it contains "env" it
|
||||
will be ignored by git.
|
||||
* If you don't have CMake 3.14+, just add "cmake" to the pip install command.
|
||||
* You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+
|
||||
* 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.
|
||||
* In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`.
|
||||
FindPython uses `-DPython_ROOT_DIR=/path/to` or
|
||||
`-DPython_EXECUTABLE=/path/to/python`.
|
||||
@ -149,8 +149,8 @@ To run the tests, you can "build" the check target:
|
||||
cmake --build build --target check
|
||||
```
|
||||
|
||||
`--target` can be spelled `-t` in CMake 3.15+. You can also run individual
|
||||
tests with these targets:
|
||||
`--target` can be spelled `-t`. You can also run individual tests with these
|
||||
targets:
|
||||
|
||||
* `pytest`: Python tests only, using the
|
||||
[pytest](https://docs.pytest.org/en/stable/) framework
|
||||
|
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@ -68,7 +68,18 @@ jobs:
|
||||
# Extra ubuntu latest job
|
||||
- runs-on: ubuntu-latest
|
||||
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 }}"
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
|
1262
.github/workflows/ci_sh_def.yml
vendored
1262
.github/workflows/ci_sh_def.yml
vendored
File diff suppressed because it is too large
Load Diff
223
.github/workflows/ci_sh_def.yml.patch
vendored
223
.github/workflows/ci_sh_def.yml.patch
vendored
@ -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"
|
||||
|
4
.github/workflows/configure.yml
vendored
4
.github/workflows/configure.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
||||
include:
|
||||
- runs-on: ubuntu-20.04
|
||||
arch: x64
|
||||
cmake: "3.5"
|
||||
cmake: "3.15"
|
||||
|
||||
- runs-on: ubuntu-20.04
|
||||
arch: x64
|
||||
@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- runs-on: macos-13
|
||||
arch: x64
|
||||
cmake: "3.8"
|
||||
cmake: "3.15"
|
||||
|
||||
- runs-on: windows-2019
|
||||
arch: x64 # x86 compilers seem to be missing on 2019 image
|
||||
|
5
.github/workflows/emscripten.yaml
vendored
5
.github/workflows/emscripten.yaml
vendored
@ -5,6 +5,9 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- stable
|
||||
- smart_holder
|
||||
- v*
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -23,8 +26,6 @@ jobs:
|
||||
- uses: pypa/cibuildwheel@v2.20
|
||||
env:
|
||||
PYODIDE_BUILD_EXPORTS: whole_archive
|
||||
CFLAGS: -fexceptions
|
||||
LDFLAGS: -fexceptions
|
||||
with:
|
||||
package-dir: tests
|
||||
only: cp312-pyodide_wasm32
|
||||
|
2
.github/workflows/pip.yml
vendored
2
.github/workflows/pip.yml
vendored
@ -103,7 +103,7 @@ jobs:
|
||||
- uses: actions/download-artifact@v4
|
||||
|
||||
- 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:
|
||||
subject-path: "*/pybind11*"
|
||||
|
||||
|
@ -10,16 +10,7 @@ if(NOT CMAKE_VERSION VERSION_LESS "3.27")
|
||||
cmake_policy(GET CMP0148 _pybind11_cmp0148)
|
||||
endif()
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# 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()
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
|
||||
if(_pybind11_cmp0148)
|
||||
cmake_policy(SET CMP0148 ${_pybind11_cmp0148})
|
||||
@ -27,9 +18,7 @@ if(_pybind11_cmp0148)
|
||||
endif()
|
||||
|
||||
# Avoid infinite recursion if tests include this as a subdirectory
|
||||
if(DEFINED PYBIND11_MASTER_PROJECT)
|
||||
return()
|
||||
endif()
|
||||
include_guard(GLOBAL)
|
||||
|
||||
# Extract project version from source
|
||||
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)
|
||||
|
||||
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}")
|
||||
|
||||
if(CMAKE_CXX_STANDARD)
|
||||
@ -133,8 +114,7 @@ cmake_dependent_option(
|
||||
"Install pybind11 headers in Python include directory instead of default installation prefix"
|
||||
OFF "PYBIND11_INSTALL" OFF)
|
||||
|
||||
cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default}
|
||||
"NOT CMAKE_VERSION VERSION_LESS 3.12" OFF)
|
||||
option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default})
|
||||
|
||||
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
|
||||
# (makes transition easier while we support both modes).
|
||||
@ -154,7 +134,7 @@ set(PYBIND11_HEADERS
|
||||
include/pybind11/detail/init.h
|
||||
include/pybind11/detail/internals.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/typeid.h
|
||||
include/pybind11/detail/using_smart_holder.h
|
||||
@ -190,7 +170,7 @@ set(PYBIND11_HEADERS
|
||||
include/pybind11/typing.h)
|
||||
|
||||
# Compare with grep and warn if mismatched
|
||||
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
|
||||
if(PYBIND11_MASTER_PROJECT)
|
||||
file(
|
||||
GLOB_RECURSE _pybind11_header_check
|
||||
LIST_DIRECTORIES false
|
||||
@ -208,10 +188,7 @@ if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# CMake 3.12 added list(TRANSFORM <list> PREPEND
|
||||
# But we can't use it yet
|
||||
string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" PYBIND11_HEADERS
|
||||
"${PYBIND11_HEADERS}")
|
||||
list(TRANSFORM PYBIND11_HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||
|
||||
# Cache variable so this can be used in parent projects
|
||||
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"
|
||||
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.14)
|
||||
# Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does
|
||||
# not depend on architecture specific settings or libraries.
|
||||
set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
|
||||
unset(CMAKE_SIZEOF_VOID_P)
|
||||
|
||||
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()
|
||||
# CMake 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)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
|
||||
|
@ -259,7 +259,7 @@ copying to take place:
|
||||
"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)``
|
||||
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
|
||||
|
@ -162,7 +162,7 @@ the declaration
|
||||
|
||||
.. 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
|
||||
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
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<int>);
|
||||
PYBIND11_MAKE_OPAQUE(std::map<std::string, double>);
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<int>)
|
||||
PYBIND11_MAKE_OPAQUE(std::map<std::string, double>)
|
||||
|
||||
// ...
|
||||
|
||||
|
@ -18,7 +18,7 @@ information, see :doc:`/compiling`.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.29)
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
project(example)
|
||||
|
||||
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`
|
||||
|
@ -131,7 +131,7 @@ top namespace level before any binding code:
|
||||
|
||||
.. 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
|
||||
placeholder name that is used as a template parameter of the second argument.
|
||||
@ -143,7 +143,7 @@ by default. Specify
|
||||
|
||||
.. 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
|
||||
risk of inconsistencies (such as multiple independent ``SmartPtr`` instances
|
||||
@ -161,7 +161,7 @@ specialized:
|
||||
.. code-block:: cpp
|
||||
|
||||
// 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
|
||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||
|
@ -78,6 +78,13 @@ For brevity, all code examples assume that the following two lines are present:
|
||||
|
||||
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.
|
||||
|
||||
.. _simple_example:
|
||||
|
@ -15,6 +15,115 @@ IN DEVELOPMENT
|
||||
|
||||
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)
|
||||
------------------------------
|
||||
|
||||
|
@ -18,7 +18,7 @@ A Python extension module can be created with just a few lines of code:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.15...3.29)
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
set(PYBIND11_FINDPYTHON ON)
|
||||
@ -319,11 +319,11 @@ Building with CMake
|
||||
|
||||
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
|
||||
the module section. Pybind11 currently supports a lower minimum if you don't
|
||||
use the modern FindPython, though be aware that CMake 3.27 removed the old
|
||||
mechanism, so pybind11 will automatically switch if the old mechanism is not
|
||||
available. Please opt into the new mechanism if at all possible. Our default
|
||||
may change in future versions. This is the minimum required:
|
||||
the module section. Pybind11 currently defaults to the old mechanism, though be
|
||||
aware that CMake 3.27 removed the old mechanism, so pybind11 will automatically
|
||||
switch if the old mechanism is not available. Please opt into the new mechanism
|
||||
if at all possible. Our default may change in future versions. This is the
|
||||
minimum required:
|
||||
|
||||
|
||||
|
||||
@ -333,6 +333,9 @@ may change in future versions. This is the minimum required:
|
||||
.. versionchanged:: 2.11
|
||||
CMake 3.5+ is required.
|
||||
|
||||
.. versionchanged:: 2.14
|
||||
CMake 3.15+ is required.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
into performance-critical parts and associated bindings. In this case, we can
|
||||
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
|
||||
|
||||
cmake_minimum_required(VERSION 3.4...3.18)
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
find_package(pybind11 REQUIRED)
|
||||
@ -483,14 +486,13 @@ can refer to the same [cmake_example]_ repository for a full sample project
|
||||
FindPython mode
|
||||
---------------
|
||||
|
||||
CMake 3.12+ (3.15+ recommended, 3.18.2+ ideal) added a new module called
|
||||
FindPython that had a highly improved search algorithm and modern targets
|
||||
and tools. If you use FindPython, pybind11 will detect this and use the
|
||||
existing targets instead:
|
||||
Modern CMake (3.18.2+ ideal) added a new module called FindPython that had a
|
||||
highly improved search algorithm and modern targets and tools. If you use
|
||||
FindPython, pybind11 will detect this and use the existing targets instead:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.15...3.22)
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
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
|
||||
|
||||
``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``
|
||||
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
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.29)
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
|
||||
@ -626,7 +628,7 @@ information about usage in C++, see :doc:`/advanced/embedding`.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.29)
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
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
|
||||
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
|
||||
must be C++14 compatible (but your implementation can use more modern constructs).
|
||||
|
||||
|
16
docs/faq.rst
16
docs/faq.rst
@ -258,9 +258,9 @@ CMake configure line. (Replace ``$(which python)`` with a path to python if
|
||||
your prefer.)
|
||||
|
||||
You can alternatively try ``-DPYBIND11_FINDPYTHON=ON``, which will activate the
|
||||
new CMake FindPython support instead of pybind11's custom search. Requires
|
||||
CMake 3.12+, and 3.15+ or 3.18.2+ are even better. You can set this in your
|
||||
``CMakeLists.txt`` before adding or finding pybind11, as well.
|
||||
new CMake FindPython support instead of pybind11's custom search. Newer CMake,
|
||||
like, 3.18.2+, is recommended. You can set this in your ``CMakeLists.txt``
|
||||
before adding or finding pybind11, as well.
|
||||
|
||||
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
|
||||
possible, the CMake machinery should be called *before* including pybind11.
|
||||
2. Set ``PYBIND11_FINDPYTHON`` to ``True`` or use ``find_package(Python
|
||||
COMPONENTS Interpreter Development)`` on modern CMake (3.12+, 3.15+ better,
|
||||
3.18.2+ best). Pybind11 in these cases uses the new CMake FindPython instead
|
||||
of the old, deprecated search tools, and these modules are much better at
|
||||
finding the correct Python. If FindPythonLibs/Interp are not available
|
||||
(CMake 3.27+), then this will be ignored and FindPython will be used.
|
||||
COMPONENTS Interpreter Development)`` on modern CMake ( 3.18.2+ best).
|
||||
Pybind11 in these cases uses the new CMake FindPython instead of the old,
|
||||
deprecated search tools, and these modules are much better at finding the
|
||||
correct Python. If FindPythonLibs/Interp are not available (CMake 3.27+),
|
||||
then this will be ignored and FindPython will be used.
|
||||
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
|
||||
yourself, because it does not know about or include things that depend on
|
||||
|
@ -957,8 +957,12 @@ public:
|
||||
using base::value;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
return base::template load_impl<copyable_holder_caster<type, std::shared_ptr<type>>>(
|
||||
src, convert);
|
||||
if (base::template load_impl<copyable_holder_caster<type, std::shared_ptr<type>>>(
|
||||
src, convert)) {
|
||||
sh_load_helper.maybe_set_python_instance_is_alias(src);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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 "
|
||||
"(inherently unsafe).");
|
||||
}
|
||||
return std::addressof(shared_ptr_holder);
|
||||
return std::addressof(shared_ptr_storage);
|
||||
}
|
||||
|
||||
explicit operator std::shared_ptr<type> &() {
|
||||
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
|
||||
@ -1013,12 +1017,13 @@ protected:
|
||||
void load_value(value_and_holder &&v_h) {
|
||||
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
sh_load_helper.loaded_v_h = v_h;
|
||||
sh_load_helper.was_populated = true;
|
||||
value = sh_load_helper.get_void_ptr_or_nullptr();
|
||||
return;
|
||||
}
|
||||
if (v_h.holder_constructed()) {
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h;
|
||||
} else {
|
||||
shared_ptr_holder
|
||||
= std::shared_ptr<type>(sub_caster.shared_ptr_holder, (type *) value);
|
||||
shared_ptr_storage
|
||||
= std::shared_ptr<type>(sub_caster.shared_ptr_storage, (type *) value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1058,8 +1063,8 @@ protected:
|
||||
|
||||
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
|
||||
std::shared_ptr<type> shared_ptr_storage;
|
||||
};
|
||||
|
||||
#endif // PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
@ -1134,20 +1139,25 @@ public:
|
||||
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);
|
||||
}
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
return base::template load_impl<
|
||||
move_only_holder_caster<type, std::unique_ptr<type, deleter>>>(src, convert);
|
||||
if (base::template load_impl<
|
||||
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) {
|
||||
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.type = typeinfo;
|
||||
sh_load_helper.was_populated = true;
|
||||
value = sh_load_helper.get_void_ptr_or_nullptr();
|
||||
return;
|
||||
}
|
||||
@ -1156,8 +1166,14 @@ public:
|
||||
+ clean_type_id(typeinfo->cpptype->name()) + ")");
|
||||
}
|
||||
|
||||
template <typename>
|
||||
using cast_op_type = std::unique_ptr<type, deleter>;
|
||||
template <typename T_>
|
||||
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>() {
|
||||
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__));
|
||||
}
|
||||
|
||||
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) {
|
||||
for (auto &cast : typeinfo->implicit_casts) {
|
||||
move_only_holder_caster sub_caster(*cast.first);
|
||||
@ -1186,6 +1224,8 @@ public:
|
||||
static bool try_direct_conversions(handle) { return false; }
|
||||
|
||||
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
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../attr.h"
|
||||
#include "../options.h"
|
||||
#include <pybind11/attr.h>
|
||||
#include <pybind11/options.h>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
@ -479,6 +479,8 @@ PYBIND11_WARNING_POP
|
||||
}
|
||||
|
||||
\endrst */
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments")
|
||||
#define PYBIND11_MODULE(name, variable, ...) \
|
||||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
|
||||
PYBIND11_MAYBE_UNUSED; \
|
||||
@ -499,6 +501,7 @@ PYBIND11_WARNING_POP
|
||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
@ -554,7 +557,7 @@ enum class return_value_policy : uint8_t {
|
||||
object without taking ownership similar to the above
|
||||
return_value_policy::reference policy. In contrast to that policy, the
|
||||
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
|
||||
reference relationship that ensures that the parent cannot be garbage
|
||||
collected while Python is still using the child. More advanced
|
||||
@ -637,6 +640,11 @@ struct instance {
|
||||
bool simple_instance_registered : 1;
|
||||
/// If true, get_internals().patients has an entry for this object
|
||||
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
|
||||
/// themselves)
|
||||
|
@ -12,10 +12,10 @@
|
||||
#include "common.h"
|
||||
|
||||
#if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
# include "../gil.h"
|
||||
# include <pybind11/gil.h>
|
||||
#endif
|
||||
|
||||
#include "../pytypes.h"
|
||||
#include <pybind11/pytypes.h>
|
||||
|
||||
#include <exception>
|
||||
#include <mutex>
|
||||
|
@ -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
|
||||
// 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`
|
||||
member invisible to the `shared_from_this` mechanism, in case the lifetime
|
||||
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
|
||||
@ -146,7 +136,6 @@ struct smart_holder {
|
||||
bool vptr_is_external_shared_ptr : 1;
|
||||
bool is_populated : 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.
|
||||
smart_holder(smart_holder &&) = default;
|
||||
@ -156,8 +145,7 @@ struct smart_holder {
|
||||
|
||||
smart_holder()
|
||||
: vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false},
|
||||
vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false},
|
||||
pointee_depends_on_holder_owner{false} {}
|
||||
vptr_is_external_shared_ptr{false}, is_populated{false}, is_disowned{false} {}
|
||||
|
||||
bool has_pointee() const { return vptr != nullptr; }
|
||||
|
||||
@ -243,6 +231,24 @@ struct smart_holder {
|
||||
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) {
|
||||
smart_holder hld;
|
||||
hld.vptr.reset(raw_ptr, [](void *) {});
|
||||
@ -256,26 +262,6 @@ struct smart_holder {
|
||||
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>
|
||||
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");
|
||||
@ -319,16 +305,6 @@ struct smart_holder {
|
||||
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>
|
||||
static smart_holder from_unique_ptr(std::unique_ptr<T, D> &&unq_ptr,
|
||||
void *void_ptr = nullptr) {
|
||||
@ -351,19 +327,6 @@ struct smart_holder {
|
||||
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>
|
||||
static smart_holder from_shared_ptr(std::shared_ptr<T> shd_ptr) {
|
||||
smart_holder hld;
|
@ -9,9 +9,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../gil.h"
|
||||
#include "../pytypes.h"
|
||||
#include "../trampoline_self_life_support.h"
|
||||
#include <pybind11/gil.h>
|
||||
#include <pybind11/pytypes.h>
|
||||
#include <pybind11/trampoline_self_life_support.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "descr.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>
|
||||
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) {
|
||||
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).");
|
||||
}
|
||||
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);
|
||||
if (vptr_gd_ptr != nullptr) {
|
||||
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
|
||||
= 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 "
|
||||
"py::trampoline_self_life_support, therefore the ownership of this "
|
||||
"instance cannot safely be transferred to C++.");
|
||||
}
|
||||
|
||||
// Temporary variable to store the extracted deleter in.
|
||||
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)));
|
||||
}
|
||||
std::unique_ptr<D> extracted_deleter = holder().template extract_deleter<T, D>(context);
|
||||
|
||||
// Critical transfer-of-ownership section. This must stay together.
|
||||
if (self_life_support != nullptr) {
|
||||
@ -819,6 +814,19 @@ struct load_helper : value_and_holder_helper {
|
||||
|
||||
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)
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
# include "smart_holder_poc.h"
|
||||
# include "struct_smart_holder.h"
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
@ -9,7 +9,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../numpy.h"
|
||||
#include <pybind11/numpy.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
|
||||
|
@ -7,7 +7,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../numpy.h"
|
||||
#include <pybind11/numpy.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
|
@ -9,12 +9,55 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define PYBIND11_HAS_TYPE_CASTER_STD_FUNCTION_SPECIALIZATIONS
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
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>
|
||||
struct type_caster<std::function<Return(Args...)>> {
|
||||
@ -77,40 +120,8 @@ public:
|
||||
// See PR #1413 for full details
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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)));
|
||||
value = type_caster_std_function_specializations::func_wrapper<Return, Args...>(
|
||||
type_caster_std_function_specializations::func_handle(std::move(func)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2278,7 +2278,8 @@ private:
|
||||
}
|
||||
auto *uninitialized_location = std::addressof(v_h.holder<holder_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;
|
||||
if (holder_void_ptr) {
|
||||
// Note: inst->owned ignored.
|
||||
@ -2288,14 +2289,12 @@ private:
|
||||
uninitialized_location, value_ptr_w_t, value_ptr_w_t)) {
|
||||
if (inst->owned) {
|
||||
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 {
|
||||
new (uninitialized_location)
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -1263,6 +1263,7 @@ protected:
|
||||
using pointer = arrow_proxy<const handle>;
|
||||
|
||||
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
|
||||
reference dereference() const { return *ptr; }
|
||||
@ -1285,6 +1286,7 @@ protected:
|
||||
using pointer = arrow_proxy<const sequence_accessor>;
|
||||
|
||||
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)}; }
|
||||
void increment() { ++index; }
|
||||
|
@ -11,10 +11,14 @@
|
||||
|
||||
#include "pybind11.h"
|
||||
#include "detail/common.h"
|
||||
#include "detail/descr.h"
|
||||
#include "detail/type_caster_base.h"
|
||||
|
||||
#include <deque>
|
||||
#include <initializer_list>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
@ -35,6 +39,89 @@
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
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
|
||||
/// forwarding a container element). Typically used indirect via forwarded_type(), below.
|
||||
template <typename T, typename U>
|
||||
@ -66,17 +153,10 @@ private:
|
||||
}
|
||||
void reserve_maybe(const anyset &, void *) {}
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<anyset>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto s = reinterpret_borrow<anyset>(src);
|
||||
value.clear();
|
||||
reserve_maybe(s, &value);
|
||||
for (auto entry : s) {
|
||||
bool convert_iterable(const iterable &itbl, bool convert) {
|
||||
for (const auto &it : itbl) {
|
||||
key_conv conv;
|
||||
if (!conv.load(entry, convert)) {
|
||||
if (!conv.load(it, convert)) {
|
||||
return false;
|
||||
}
|
||||
value.insert(cast_op<Key &&>(std::move(conv)));
|
||||
@ -84,6 +164,29 @@ public:
|
||||
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>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
if (!std::is_lvalue_reference<T>::value) {
|
||||
@ -115,15 +218,10 @@ private:
|
||||
}
|
||||
void reserve_maybe(const dict &, void *) {}
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<dict>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto d = reinterpret_borrow<dict>(src);
|
||||
bool convert_elements(const dict &d, bool convert) {
|
||||
value.clear();
|
||||
reserve_maybe(d, &value);
|
||||
for (auto it : d) {
|
||||
for (const auto &it : d) {
|
||||
key_conv kconv;
|
||||
value_conv vconv;
|
||||
if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) {
|
||||
@ -134,6 +232,25 @@ public:
|
||||
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>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
dict d;
|
||||
@ -166,20 +283,21 @@ struct list_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src)) {
|
||||
if (!PyObjectTypeIsConvertibleToStdVector(src.ptr())) {
|
||||
return false;
|
||||
}
|
||||
auto s = reinterpret_borrow<sequence>(src);
|
||||
value.clear();
|
||||
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)));
|
||||
if (isinstance<sequence>(src)) {
|
||||
return convert_elements(src, convert);
|
||||
}
|
||||
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:
|
||||
@ -189,6 +307,20 @@ private:
|
||||
}
|
||||
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:
|
||||
template <typename T>
|
||||
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>
|
||||
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>
|
||||
struct array_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
private:
|
||||
template <bool R = Resizable>
|
||||
bool require_size(enable_if_t<R, size_t> size) {
|
||||
if (value.size() != size) {
|
||||
value.resize(size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
template <bool R = Resizable>
|
||||
bool require_size(enable_if_t<!R, size_t> size) {
|
||||
return size == Size;
|
||||
}
|
||||
std::unique_ptr<ArrayType> value;
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<sequence>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto l = reinterpret_borrow<sequence>(src);
|
||||
if (!require_size(l.size())) {
|
||||
return false;
|
||||
}
|
||||
template <bool R = Resizable, enable_if_t<R, int> = 0>
|
||||
bool convert_elements(handle seq, bool convert) {
|
||||
auto l = reinterpret_borrow<sequence>(seq);
|
||||
value.reset(new ArrayType{});
|
||||
// Using `resize` to preserve the behavior exactly as it was before PR #5305
|
||||
// For the `resize` to work, `Value` must be default constructible.
|
||||
// For `std::valarray`, this is a requirement:
|
||||
// https://en.cppreference.com/w/cpp/named_req/NumericType
|
||||
value->resize(l.size());
|
||||
size_t ctr = 0;
|
||||
for (const auto &it : l) {
|
||||
value_conv conv;
|
||||
if (!conv.load(it, convert)) {
|
||||
return false;
|
||||
}
|
||||
value[ctr++] = cast_op<Value &&>(std::move(conv));
|
||||
(*value)[ctr++] = cast_op<Value &&>(std::move(conv));
|
||||
}
|
||||
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>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
list l(src.size());
|
||||
@ -272,12 +448,36 @@ public:
|
||||
return l.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(ArrayType,
|
||||
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(")]")));
|
||||
// Code copied from PYBIND11_TYPE_CASTER macro.
|
||||
// Intentionally preserving the behavior exactly as it was before PR #5305
|
||||
template <typename T_, enable_if_t<std::is_same<ArrayType, remove_cv_t<T_>>::value, int> = 0>
|
||||
static handle cast(T_ *src, return_value_policy policy, handle parent) {
|
||||
if (!src) {
|
||||
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>
|
||||
|
@ -4,11 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../pybind11.h"
|
||||
#include "../detail/common.h"
|
||||
#include "../detail/descr.h"
|
||||
#include "../cast.h"
|
||||
#include "../pytypes.h"
|
||||
#include <pybind11/cast.h>
|
||||
#include <pybind11/detail/common.h>
|
||||
#include <pybind11/detail/descr.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/pytypes.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -2,12 +2,35 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
import sysconfig
|
||||
|
||||
from ._version import __version__
|
||||
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:
|
||||
dirs = [
|
||||
@ -22,7 +45,7 @@ def print_includes() -> None:
|
||||
if d and d not in unique_dirs:
|
||||
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:
|
||||
@ -54,9 +77,9 @@ def main() -> None:
|
||||
if args.includes:
|
||||
print_includes()
|
||||
if args.cmakedir:
|
||||
print(get_cmake_dir())
|
||||
print(quote(get_cmake_dir()))
|
||||
if args.pkgconfigdir:
|
||||
print(get_pkgconfig_dir())
|
||||
print(quote(get_pkgconfig_dir()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -5,16 +5,7 @@
|
||||
# All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# 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()
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
|
||||
# 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")
|
||||
|
||||
option(PYBIND11_WERROR "Report all warnings as errors" OFF)
|
||||
option(DOWNLOAD_EIGEN "Download EIGEN (requires CMake 3.11+)" OFF)
|
||||
option(PYBIND11_CUDA_TESTS "Enable building CUDA tests (requires CMake 3.12+)" OFF)
|
||||
option(DOWNLOAD_EIGEN "Download EIGEN" OFF)
|
||||
option(PYBIND11_CUDA_TESTS "Enable building CUDA tests" OFF)
|
||||
set(PYBIND11_TEST_OVERRIDE
|
||||
""
|
||||
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_inheritance
|
||||
test_class_sh_mi_thunks
|
||||
test_class_sh_module_local.py
|
||||
test_class_sh_property
|
||||
test_class_sh_property_non_owning
|
||||
test_class_sh_shared_ptr_copy_move
|
||||
@ -178,6 +168,7 @@ set(PYBIND11_TEST_FILES
|
||||
test_tagbased_polymorphic
|
||||
test_thread
|
||||
test_type_caster_pyobject_ptr
|
||||
test_type_caster_std_function_specializations
|
||||
test_union
|
||||
test_unnamed_namespace_a
|
||||
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.
|
||||
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_class_sh_module_local.py"
|
||||
"class_sh_module_local_0;class_sh_module_local_1;class_sh_module_local_2")
|
||||
|
||||
set(PYBIND11_EIGEN_REPO
|
||||
"https://gitlab.com/libeigen/eigen.git"
|
||||
@ -270,25 +259,21 @@ endif()
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
# 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
|
||||
# produces a fatal error if loaded from a pre-3.0 cmake.
|
||||
if(DOWNLOAD_EIGEN)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
||||
message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN")
|
||||
if(CMAKE_VERSION VERSION_LESS 3.18)
|
||||
set(_opts)
|
||||
else()
|
||||
set(_opts SOURCE_SUBDIR no-cmake-build)
|
||||
endif()
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
eigen
|
||||
GIT_REPOSITORY "${PYBIND11_EIGEN_REPO}"
|
||||
GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}")
|
||||
|
||||
FetchContent_GetProperties(eigen)
|
||||
if(NOT eigen_POPULATED)
|
||||
message(
|
||||
STATUS
|
||||
"Downloading Eigen ${PYBIND11_EIGEN_VERSION_STRING} (${PYBIND11_EIGEN_VERSION_HASH}) from ${PYBIND11_EIGEN_REPO}"
|
||||
)
|
||||
FetchContent_Populate(eigen)
|
||||
GIT_TAG "${PYBIND11_EIGEN_VERSION_HASH}"
|
||||
${_opts})
|
||||
FetchContent_MakeAvailable(eigen)
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.18)
|
||||
set(EIGEN3_INCLUDE_DIR "${eigen_SOURCE_DIR}")
|
||||
endif()
|
||||
|
||||
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)
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
endif()
|
||||
message(
|
||||
STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download")
|
||||
message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON to download")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -417,6 +401,9 @@ function(pybind11_enable_warnings target_name)
|
||||
-Wdeprecated
|
||||
-Wundef
|
||||
-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()
|
||||
|
||||
if(PYBIND11_WERROR)
|
||||
@ -522,12 +509,10 @@ foreach(target ${test_targets})
|
||||
endforeach()
|
||||
|
||||
# Provide nice organisation in IDEs
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.8)
|
||||
source_group(
|
||||
TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
||||
PREFIX "Header Files"
|
||||
FILES ${PYBIND11_HEADERS})
|
||||
endif()
|
||||
source_group(
|
||||
TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
||||
PREFIX "Header Files"
|
||||
FILES ${PYBIND11_HEADERS})
|
||||
|
||||
# Make sure pytest is found or produce a warning
|
||||
pybind11_find_import(pytest VERSION 3.1)
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -9,7 +9,6 @@ import tarfile
|
||||
import zipfile
|
||||
|
||||
# These tests must be run explicitly
|
||||
# They require CMake 3.15+ (--install)
|
||||
|
||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
|
||||
@ -61,7 +60,7 @@ detail_headers = {
|
||||
"include/pybind11/detail/init.h",
|
||||
"include/pybind11/detail/internals.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/typeid.h",
|
||||
"include/pybind11/detail/using_smart_holder.h",
|
||||
|
@ -56,13 +56,13 @@ private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
PYBIND11_MAKE_OPAQUE(LocalVec);
|
||||
PYBIND11_MAKE_OPAQUE(LocalVec2);
|
||||
PYBIND11_MAKE_OPAQUE(LocalMap);
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalVec);
|
||||
// PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalMap);
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalMap2);
|
||||
PYBIND11_MAKE_OPAQUE(LocalVec)
|
||||
PYBIND11_MAKE_OPAQUE(LocalVec2)
|
||||
PYBIND11_MAKE_OPAQUE(LocalMap)
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalVec)
|
||||
// PYBIND11_MAKE_OPAQUE(NonLocalVec2) // same type as LocalVec2
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalMap)
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalMap2)
|
||||
|
||||
// Simple bindings (used with the above):
|
||||
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 i.i + Adjust;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Simulate a foreign library base class (to match the example in the docs):
|
||||
namespace pets {
|
||||
|
51
tests/pure_cpp/smart_holder_poc.h
Normal file
51
tests/pure_cpp/smart_holder_poc.h
Normal 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
|
@ -1,6 +1,4 @@
|
||||
#define PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP
|
||||
|
||||
#include "pybind11/detail/smart_holder_poc.h"
|
||||
#include "smart_holder_poc.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@ -16,6 +14,7 @@
|
||||
#include "catch.hpp"
|
||||
|
||||
using pybindit::memory::smart_holder;
|
||||
namespace poc = pybindit::memory::smart_holder_poc;
|
||||
|
||||
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]") {
|
||||
static int value = 19;
|
||||
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]") {
|
||||
helpers::movable_int orig(19);
|
||||
{
|
||||
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(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]") {
|
||||
static int value = 19;
|
||||
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).");
|
||||
}
|
||||
|
||||
TEST_CASE("from_raw_ptr_unowned+as_unique_ptr", "[E]") {
|
||||
static int value = 19;
|
||||
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).");
|
||||
}
|
||||
|
||||
TEST_CASE("from_raw_ptr_unowned+as_unique_ptr_with_deleter", "[E]") {
|
||||
static int value = 19;
|
||||
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).");
|
||||
}
|
||||
|
||||
@ -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]") {
|
||||
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
|
||||
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]") {
|
||||
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(*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]") {
|
||||
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
|
||||
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).");
|
||||
}
|
||||
|
||||
TEST_CASE("from_raw_ptr_take_ownership+as_unique_ptr1", "[S]") {
|
||||
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(*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]") {
|
||||
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
|
||||
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]") {
|
||||
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).");
|
||||
}
|
||||
|
||||
@ -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));
|
||||
std::unique_ptr<int> new_owner(hld.as_raw_ptr_unowned<int>());
|
||||
hld.disown();
|
||||
REQUIRE(hld.as_lvalue_ref<int>() == 19);
|
||||
REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
|
||||
REQUIRE(*new_owner == 19);
|
||||
hld.reclaim_disowned(); // Manually veriified: without this, clang++ -fsanitize=address reports
|
||||
// "detected memory leaks".
|
||||
// NOLINTNEXTLINE(bugprone-unused-return-value)
|
||||
(void) new_owner.release(); // Manually verified: without this, clang++ -fsanitize=address
|
||||
// reports "attempting double-free".
|
||||
REQUIRE(hld.as_lvalue_ref<int>() == 19);
|
||||
REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
|
||||
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));
|
||||
std::unique_ptr<int> new_owner(hld.as_raw_ptr_unowned<int>());
|
||||
hld.disown();
|
||||
REQUIRE(hld.as_lvalue_ref<int>() == 19);
|
||||
REQUIRE(poc::as_lvalue_ref<int>(hld) == 19);
|
||||
REQUIRE(*new_owner == 19);
|
||||
hld.release_disowned();
|
||||
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));
|
||||
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
|
||||
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]") {
|
||||
std::unique_ptr<int> orig_owner(new int(19));
|
||||
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
|
||||
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(*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));
|
||||
REQUIRE(orig_owner.get() == nullptr);
|
||||
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).");
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ TEST_CASE("from_unique_ptr+as_unique_ptr1", "[S]") {
|
||||
std::unique_ptr<int> orig_owner(new int(19));
|
||||
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
|
||||
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(*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));
|
||||
REQUIRE(orig_owner.get() == nullptr);
|
||||
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]") {
|
||||
std::unique_ptr<int> orig_owner(new int(19));
|
||||
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
|
||||
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).");
|
||||
}
|
||||
|
||||
@ -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());
|
||||
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
|
||||
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(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));
|
||||
REQUIRE(orig_owner.get() == nullptr);
|
||||
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).");
|
||||
}
|
||||
|
||||
@ -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));
|
||||
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
|
||||
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]") {
|
||||
@ -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; });
|
||||
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
|
||||
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]") {
|
||||
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));
|
||||
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).");
|
||||
}
|
||||
|
||||
@ -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));
|
||||
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
|
||||
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).");
|
||||
}
|
||||
|
||||
@ -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));
|
||||
REQUIRE(orig_owner.get() == nullptr);
|
||||
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(*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));
|
||||
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
|
||||
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).");
|
||||
}
|
||||
|
||||
@ -330,27 +331,27 @@ TEST_CASE("from_unique_ptr_with_deleter+as_shared_ptr", "[S]") {
|
||||
TEST_CASE("from_shared_ptr+as_lvalue_ref", "[S]") {
|
||||
std::shared_ptr<int> orig_owner(new int(19));
|
||||
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]") {
|
||||
std::shared_ptr<int> orig_owner(new int(19));
|
||||
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).");
|
||||
}
|
||||
|
||||
TEST_CASE("from_shared_ptr+as_unique_ptr", "[E]") {
|
||||
std::shared_ptr<int> orig_owner(new int(19));
|
||||
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).");
|
||||
}
|
||||
|
||||
TEST_CASE("from_shared_ptr+as_unique_ptr_with_deleter", "[E]") {
|
||||
std::shared_ptr<int> orig_owner(new int(19));
|
||||
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).");
|
||||
}
|
||||
|
||||
@ -362,19 +363,19 @@ TEST_CASE("from_shared_ptr+as_shared_ptr", "[S]") {
|
||||
|
||||
TEST_CASE("error_unpopulated_holder", "[E]") {
|
||||
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]") {
|
||||
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
|
||||
hld.as_unique_ptr<int>();
|
||||
REQUIRE_THROWS_WITH(hld.as_lvalue_ref<int>(), "Disowned holder (as_lvalue_ref).");
|
||||
poc::as_unique_ptr<int>(hld);
|
||||
REQUIRE_THROWS_WITH(poc::as_lvalue_ref<int>(hld), "Disowned holder (as_lvalue_ref).");
|
||||
}
|
||||
|
||||
TEST_CASE("error_cannot_disown_nullptr", "[E]") {
|
||||
auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19));
|
||||
hld.as_unique_ptr<int>();
|
||||
REQUIRE_THROWS_WITH(hld.as_unique_ptr<int>(), "Cannot disown nullptr (as_unique_ptr).");
|
||||
poc::as_unique_ptr<int>(hld);
|
||||
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]") {
|
||||
|
@ -148,7 +148,7 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
m.def("dummy_function2", [](int i, int j) { return i + j; });
|
||||
m.def(
|
||||
"roundtrip",
|
||||
[](std::function<int(int)> f, bool expect_none = false) {
|
||||
[](std::function<int(int)> f, bool expect_none) {
|
||||
if (expect_none && f) {
|
||||
throw std::runtime_error("Expected None to be converted to empty std::function");
|
||||
}
|
||||
|
@ -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::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) {
|
||||
return obj;
|
||||
}
|
||||
@ -217,6 +228,9 @@ TEST_SUBMODULE(class_sh_basic, m) {
|
||||
m.def("get_ptr", get_ptr); // pass_cref
|
||||
|
||||
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);
|
||||
|
||||
py::classh<SharedPtrStash>(m, "SharedPtrStash")
|
||||
|
@ -151,19 +151,31 @@ def test_unique_ptr_roundtrip(num_round_trips=1000):
|
||||
id_orig = id_rtrn
|
||||
|
||||
|
||||
# This currently fails, because a unique_ptr is always loaded by value
|
||||
# due to pybind11/detail/smart_holder_type_casters.h:689
|
||||
# I think, we need to provide more cast operators.
|
||||
@pytest.mark.skip()
|
||||
def test_unique_ptr_cref_roundtrip():
|
||||
orig = m.atyp("passenger")
|
||||
id_orig = id(orig)
|
||||
mtxt_orig = m.get_mtxt(orig)
|
||||
def test_pass_unique_ptr_cref():
|
||||
obj = m.atyp("ctor_arg")
|
||||
assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.get_mtxt(obj))
|
||||
assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.pass_unique_ptr_cref(obj))
|
||||
assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.get_mtxt(obj))
|
||||
|
||||
recycled = m.unique_ptr_cref_roundtrip(orig)
|
||||
assert m.get_mtxt(orig) == mtxt_orig
|
||||
assert m.get_mtxt(recycled) == mtxt_orig
|
||||
assert id(recycled) == id_orig
|
||||
|
||||
def test_rtrn_unique_ptr_cref():
|
||||
obj0 = m.rtrn_unique_ptr_cref("")
|
||||
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(
|
||||
|
@ -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!"
|
||||
)
|
@ -87,7 +87,12 @@ long pass_shared_ptr(const std::shared_ptr<Sft> &obj) {
|
||||
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}; }
|
||||
|
||||
@ -135,7 +140,8 @@ TEST_SUBMODULE(class_sh_trampoline_shared_from_this, m) {
|
||||
|
||||
m.def("use_count", use_count);
|
||||
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_unq_ptr", make_pure_cpp_sft_unq_ptr);
|
||||
m.def("make_pure_cpp_sft_shd_ptr", make_pure_cpp_sft_shd_ptr);
|
||||
|
@ -137,11 +137,14 @@ def test_pass_released_shared_ptr_as_unique_ptr():
|
||||
obj = PySft("PySft")
|
||||
stash1 = m.SftSharedPtrStash(1)
|
||||
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:
|
||||
m.pass_unique_ptr(obj)
|
||||
m.pass_unique_ptr_rref(obj)
|
||||
assert str(exc_info.value) == (
|
||||
"Python instance is currently owned by a std::shared_ptr."
|
||||
)
|
||||
assert obj.history == "PySft_Stash1Add"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -1,13 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# 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()
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
|
||||
project(test_installed_embed CXX)
|
||||
|
||||
|
@ -1,14 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
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()
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
|
||||
project(test_installed_function CXX)
|
||||
|
||||
|
@ -1,13 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# 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()
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
|
||||
project(test_installed_target CXX)
|
||||
|
||||
|
@ -1,13 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# 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()
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
|
||||
project(test_subdirectory_embed CXX)
|
||||
|
||||
|
@ -1,13 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# 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()
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
|
||||
project(test_subdirectory_function CXX)
|
||||
|
||||
|
@ -1,13 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# 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()
|
||||
cmake_minimum_required(VERSION 3.15...3.30)
|
||||
|
||||
project(test_subdirectory_target CXX)
|
||||
|
||||
|
@ -55,7 +55,7 @@ void reset_refs() {
|
||||
}
|
||||
|
||||
// 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
|
||||
// reference is referencing rows/columns correctly).
|
||||
@ -76,7 +76,7 @@ struct CustomOperatorNew {
|
||||
Eigen::Matrix4d a = Eigen::Matrix4d::Zero();
|
||||
Eigen::Matrix4d b = Eigen::Matrix4d::Identity();
|
||||
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(eigen_matrix, m) {
|
||||
|
@ -18,7 +18,7 @@
|
||||
// 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`
|
||||
// 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>>;
|
||||
|
||||
|
@ -13,6 +13,14 @@
|
||||
|
||||
#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 detail {
|
||||
bool check(PyObject *o) { return PyFloat_Check(o) != 0; }
|
||||
@ -923,4 +931,59 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
#else
|
||||
m.attr("defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL") = false;
|
||||
#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
|
||||
}
|
||||
|
@ -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_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
|
||||
|
@ -86,8 +86,8 @@ private:
|
||||
};
|
||||
using NonCopyableIntPair = std::pair<NonCopyableInt, NonCopyableInt>;
|
||||
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>);
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>);
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>)
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>)
|
||||
|
||||
template <typename PythonType>
|
||||
py::list test_random_access_iterator(PythonType x) {
|
||||
|
@ -11,6 +11,9 @@
|
||||
#include "object.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
// This breaks on PYBIND11_DECLARE_HOLDER_TYPE
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wpedantic")
|
||||
|
||||
namespace {
|
||||
|
||||
// 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
|
||||
|
||||
// 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:
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_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, shared_ptr_with_addressof_operator<T>);
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>);
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_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, shared_ptr_with_addressof_operator<T>)
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>)
|
||||
|
||||
TEST_SUBMODULE(smart_ptr, m) {
|
||||
// Please do not interleave `struct` and `class` definitions with bindings code,
|
||||
|
@ -59,7 +59,7 @@ struct visit_helper<boost::variant> {
|
||||
} // namespace PYBIND11_NAMESPACE
|
||||
#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
|
||||
struct TplCtorClass {
|
||||
@ -167,6 +167,14 @@ struct type_caster<ReferenceSensitiveOptional<T>>
|
||||
} // namespace detail
|
||||
} // 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_vector
|
||||
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("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
|
||||
m.def("cast_valarray", []() { return std::valarray<int>{1, 4, 9}; });
|
||||
m.def("load_valarray", [](const std::valarray<int> &v) {
|
||||
@ -546,4 +571,30 @@ TEST_SUBMODULE(stl, m) {
|
||||
[]() { return new std::vector<bool>(4513); },
|
||||
// Without explicitly specifying `take_ownership`, this function leaks.
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
@ -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):
|
||||
"""std::valarray <-> list"""
|
||||
lst = m.cast_valarray()
|
||||
@ -381,3 +388,129 @@ def test_return_vector_bool_raw_ptr():
|
||||
v = m.return_vector_bool_raw_ptr()
|
||||
assert isinstance(v, list)
|
||||
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)
|
||||
|
@ -145,4 +145,4 @@ TEST_SUBMODULE(tagbased_polymorphic, m) {
|
||||
.def(py::init<std::string>())
|
||||
.def("purr", &Panther::purr);
|
||||
m.def("create_zoo", &create_zoo);
|
||||
};
|
||||
}
|
||||
|
46
tests/test_type_caster_std_function_specializations.cpp
Normal file
46
tests/test_type_caster_std_function_specializations.cpp
Normal 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(); });
|
||||
}
|
15
tests/test_type_caster_std_function_specializations.py
Normal file
15
tests/test_type_caster_std_function_specializations.py
Normal 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
|
@ -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)
|
||||
m.def("test_gil", &test_gil);
|
||||
m.def("test_gil_from_thread", &test_gil_from_thread);
|
||||
};
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
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::embed - Adds embed links
|
||||
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) (pre-CMake 3.10)
|
||||
if(TARGET pybind11::pybind11)
|
||||
return()
|
||||
endif()
|
||||
include_guard(GLOBAL)
|
||||
|
||||
# If we are in subdirectory mode, all IMPORTED targets must be GLOBAL. If we
|
||||
# are in CONFIG mode, they should be "normal" targets instead.
|
||||
@ -75,26 +71,36 @@ set_property(
|
||||
APPEND
|
||||
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 ---------------------------
|
||||
|
||||
add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global})
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.13)
|
||||
# In CMake 3.11+, you can set INTERFACE properties via the normal methods, and
|
||||
# this would be simpler.
|
||||
set_property(
|
||||
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()
|
||||
set_property(
|
||||
TARGET pybind11::python_link_helper
|
||||
APPEND
|
||||
PROPERTY INTERFACE_LINK_OPTIONS "$<$<PLATFORM_ID:Darwin>:LINKER:-undefined,dynamic_lookup>")
|
||||
|
||||
# ------------------------ 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
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # no Clang no Intel
|
||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
||||
set_property(
|
||||
TARGET pybind11::windows_extras
|
||||
APPEND
|
||||
PROPERTY INTERFACE_COMPILE_OPTIONS $<$<NOT:$<CONFIG:Debug>>:/MP>)
|
||||
else()
|
||||
# Only set these options for C++ files. This is important so that, for
|
||||
# instance, projects that include other types of source files like CUDA
|
||||
# .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()
|
||||
# Only set these options for C++ files. This is important so that, for
|
||||
# instance, projects that include other types of source files like CUDA
|
||||
# .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()
|
||||
|
||||
@ -329,7 +327,7 @@ function(_pybind11_generate_lto target prefer_thin_lto)
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64")
|
||||
# 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
|
||||
set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_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(not_debug "$<NOT:${is_debug}>")
|
||||
set(cxx_lang "$<COMPILE_LANGUAGE:CXX>")
|
||||
if(MSVC AND CMAKE_VERSION VERSION_LESS 3.11)
|
||||
set(genex "${not_debug}")
|
||||
else()
|
||||
set(genex "$<AND:${not_debug},${cxx_lang}>")
|
||||
endif()
|
||||
set(genex "$<AND:${not_debug},${cxx_lang}>")
|
||||
set_property(
|
||||
TARGET ${target}
|
||||
APPEND
|
||||
@ -390,17 +384,10 @@ function(_pybind11_generate_lto target prefer_thin_lto)
|
||||
endif()
|
||||
|
||||
if(PYBIND11_LTO_LINKER_FLAGS)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
||||
set_property(
|
||||
TARGET ${target}
|
||||
APPEND
|
||||
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()
|
||||
set_property(
|
||||
TARGET ${target}
|
||||
APPEND
|
||||
PROPERTY INTERFACE_LINK_OPTIONS "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
@ -84,7 +84,7 @@ you can either use the basic targets, or use the FindPython tools:
|
||||
|
||||
# Python method:
|
||||
Python_add_library(MyModule2 src2.cpp)
|
||||
target_link_libraries(MyModule2 pybind11::headers)
|
||||
target_link_libraries(MyModule2 PUBLIC pybind11::headers)
|
||||
set_target_properties(MyModule2 PROPERTIES
|
||||
INTERPROCEDURAL_OPTIMIZATION ON
|
||||
CXX_VISIBILITY_PRESET ON
|
||||
|
@ -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)
|
||||
|
||||
|
@ -5,10 +5,6 @@
|
||||
# All rights reserved. Use of this source code is governed by a
|
||||
# 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)
|
||||
|
||||
get_property(
|
||||
@ -236,7 +232,6 @@ if(TARGET ${_Python}::Python)
|
||||
PROPERTY INTERFACE_LINK_LIBRARIES ${_Python}::Python)
|
||||
endif()
|
||||
|
||||
# CMake 3.15+ has this
|
||||
if(TARGET ${_Python}::Module)
|
||||
set_property(
|
||||
TARGET pybind11::module
|
||||
|
@ -5,13 +5,7 @@
|
||||
# All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
# include_guard(global) (pre-CMake 3.10)
|
||||
if(TARGET pybind11::python_headers)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Built-in in CMake 3.5+
|
||||
include(CMakeParseArguments)
|
||||
include_guard(GLOBAL)
|
||||
|
||||
if(pybind11_FIND_QUIETLY)
|
||||
set(_pybind11_quiet QUIET)
|
||||
@ -116,36 +110,19 @@ if(PYTHON_IS_DEBUG)
|
||||
PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG)
|
||||
endif()
|
||||
|
||||
# The <3.11 code here does not support release/debug builds at the same time, like on vcpkg
|
||||
if(CMAKE_VERSION VERSION_LESS 3.11)
|
||||
set_property(
|
||||
TARGET pybind11::module
|
||||
APPEND
|
||||
PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
pybind11::python_link_helper
|
||||
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:$<BUILD_INTERFACE:${PYTHON_LIBRARIES}>>"
|
||||
)
|
||||
# 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>")
|
||||
|
||||
set_property(
|
||||
TARGET pybind11::embed
|
||||
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()
|
||||
target_link_libraries(pybind11::embed INTERFACE pybind11::pybind11
|
||||
pybind11::_ClassicPythonLibraries)
|
||||
|
||||
function(pybind11_extension name)
|
||||
# The prefix and extension are provided by FindPythonLibsNew.cmake
|
||||
|
@ -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
|
||||
# Run using `cmake -P tools/test-pybind11GuessPythonExtSuffix.cmake`
|
||||
|
Loading…
Reference in New Issue
Block a user