Compare commits

..

4 Commits

Author SHA1 Message Date
Lofty
04be4bb082 hercules: global buffers 2022-09-22 23:26:35 +01:00
Lofty
a50d91e68d rbufx6 clock buffer 2022-09-22 23:26:35 +01:00
Lofty
5e86806b73 rbufx6 clock buffer 2022-09-19 02:55:29 +01:00
Lofty
1b16453d26 hercules: Viaduct arch for the HME-M7 2022-09-15 16:33:52 +01:00
750 changed files with 41418 additions and 96212 deletions

View File

@ -1,23 +0,0 @@
#!/bin/bash
function get_dependencies {
:
}
function build_nextpnr {
mkdir build
pushd build
cmake .. -DARCH=himbaechel -DHIMBAECHEL_UARCH=example -DHIMBAECHEL_EXAMPLE_DEVICES=example
make nextpnr-himbaechel -j`nproc`
popd
}
function run_tests {
:
}
function run_archcheck {
pushd build
./nextpnr-himbaechel --device EXAMPLE --test
popd
}

64
.github/ci/build_interchange.sh vendored Executable file
View File

@ -0,0 +1,64 @@
#!/bin/bash
# Install capnproto libraries
function build_capnp {
curl -O https://capnproto.org/capnproto-c++-0.8.0.tar.gz
tar zxf capnproto-c++-0.8.0.tar.gz
pushd capnproto-c++-0.8.0
./configure
make -j`nproc` check
sudo make install
popd
git clone https://github.com/capnproto/capnproto-java.git
pushd capnproto-java
make -j`nproc`
sudo make install
popd
}
# Install latest Yosys
function build_yosys {
DESTDIR=`pwd`/.yosys
pushd yosys
make -j`nproc`
sudo make install DESTDIR=$DESTDIR
popd
}
function get_dependencies {
# Install python-fpga-interchange libraries
git clone -b ${PYTHON_INTERCHANGE_TAG} https://github.com/SymbiFlow/python-fpga-interchange.git ${PYTHON_INTERCHANGE_PATH}
pushd ${PYTHON_INTERCHANGE_PATH}
git submodule update --init --recursive
python3 -m pip install -r requirements.txt
popd
if [ ${DEVICE} == "LIFCL-17" ] || [ ${DEVICE} == "LIFCL-40" ]; then
# Install prjoxide
curl --proto '=https' -sSf https://sh.rustup.rs | sh -s -- -y
git clone --recursive https://github.com/gatecat/prjoxide.git
pushd prjoxide/libprjoxide
# TODO: use a tag instead of a commit, like python-fpga-interchange
git reset --hard ${PRJOXIDE_REVISION}
PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide --all-features
popd
else
# Install RapidWright
git clone https://github.com/Xilinx/RapidWright.git ${RAPIDWRIGHT_PATH}
pushd ${RAPIDWRIGHT_PATH}
./gradlew updateJars --no-watch-fs
make compile
popd
fi
}
function build_nextpnr {
build_capnp
mkdir build
pushd build
cmake .. -DARCH=fpga_interchange -DRAPIDWRIGHT_PATH=${RAPIDWRIGHT_PATH} -DPYTHON_INTERCHANGE_PATH=${PYTHON_INTERCHANGE_PATH} -DWERROR=on
make nextpnr-fpga_interchange -j`nproc`
popd
}

View File

@ -17,8 +17,5 @@ function run_tests {
} }
function run_archcheck { function run_archcheck {
pushd build :
./nextpnr-machxo2 --device LCMXO2-1200HC-4SG32C --test
./nextpnr-machxo2 --device LCMXO3LF-6900C-6BG256C --test
popd
} }

View File

@ -9,35 +9,34 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
arch: [mistral, ice40, ecp5, generic, nexus, machxo2, gowin, himbaechel] arch: [mistral, ice40, ecp5, generic, nexus, machxo2, gowin]
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
DEPS_PATH: ${{ github.workspace }}/deps DEPS_PATH: ${{ github.workspace }}/deps
YOSYS_REVISION: 7045cf509e1d95cbc973746674cf2d7c73c02e50 YOSYS_REVISION: bd7ee79486d4e8788f36de8c25a3fb2df451d682
ICESTORM_REVISION: 68044cc4dac829729ccd0ee88d0780525b515746 ICESTORM_REVISION: 9f66f9ce16941c6417813cb87653c735a78b53ae
TRELLIS_REVISION: 36c615d1740473cc3574464c7f0bed44da20e5b6 TRELLIS_REVISION: 48486ebd1e03e4ac42c96299e881adf9d43bc241
PRJOXIDE_REVISION: c3fb1526cf4a2165e15b74f4a994d153c7695fe4 PRJOXIDE_REVISION: c3fb1526cf4a2165e15b74f4a994d153c7695fe4
MISTRAL_REVISION: ebfc0dd2cc7d6d2159b641a397c88554840e93c9 MISTRAL_REVISION: ebfc0dd2cc7d6d2159b641a397c88554840e93c9
APYCULA_REVISION: 0.8.2a1 APYCULA_REVISION: 0.2a4
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
- uses: actions/setup-python@v5 - uses: actions/setup-python@v2
with:
python-version: '3.12'
- name: Install - name: Install
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install git make cmake libboost-all-dev python3-dev pypy3 libeigen3-dev tcl-dev lzma-dev libftdi-dev clang bison flex swig qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools iverilog libreadline-dev liblzma-dev sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev lzma-dev libftdi-dev clang bison flex swig qt5-default iverilog
- name: Cache yosys installation - name: Cache yosys installation
uses: actions/cache@v4 uses: actions/cache@v2
id: cache-yosys id: cache-yosys
with: with:
path: .yosys path: .yosys
key: cache-yosys-${{ env.YOSYS_REVISION }}-r3 key: cache-yosys-${{ env.YOSYS_REVISION }}-r2
- name: Build yosys - name: Build yosys
run: | run: |
@ -46,11 +45,11 @@ jobs:
if: steps.cache-yosys.outputs.cache-hit != 'true' if: steps.cache-yosys.outputs.cache-hit != 'true'
- name: Cache icestorm installation - name: Cache icestorm installation
uses: actions/cache@v4 uses: actions/cache@v2
id: cache-icestorm id: cache-icestorm
with: with:
path: .icestorm path: .icestorm
key: cache-icestorm-${{ env.ICESTORM_REVISION }}-r3 key: cache-icestorm-${{ env.ICESTORM_REVISION }}
if: matrix.arch == 'ice40' if: matrix.arch == 'ice40'
- name: Build icestorm - name: Build icestorm
@ -60,11 +59,11 @@ jobs:
if: matrix.arch == 'ice40' && steps.cache-icestorm.outputs.cache-hit != 'true' if: matrix.arch == 'ice40' && steps.cache-icestorm.outputs.cache-hit != 'true'
- name: Cache trellis installation - name: Cache trellis installation
uses: actions/cache@v4 uses: actions/cache@v2
id: cache-trellis id: cache-trellis
with: with:
path: .trellis path: .trellis
key: cache-trellis-${{ env.TRELLIS_REVISION }}-r3 key: cache-trellis-${{ env.TRELLIS_REVISION }}
if: matrix.arch == 'ecp5' || matrix.arch == 'machxo2' if: matrix.arch == 'ecp5' || matrix.arch == 'machxo2'
- name: Build trellis - name: Build trellis
@ -74,11 +73,11 @@ jobs:
if: (matrix.arch == 'ecp5' || matrix.arch == 'machxo2') && steps.cache-trellis.outputs.cache-hit != 'true' if: (matrix.arch == 'ecp5' || matrix.arch == 'machxo2') && steps.cache-trellis.outputs.cache-hit != 'true'
- name: Cache prjoxide installation - name: Cache prjoxide installation
uses: actions/cache@v4 uses: actions/cache@v2
id: cache-prjoxide id: cache-prjoxide
with: with:
path: .prjoxide path: .prjoxide
key: cache-prjoxide-${{ env.PRJOXIDE_REVISION }}-r3 key: cache-prjoxide-${{ env.PRJOXIDE_REVISION }}
if: matrix.arch == 'nexus' if: matrix.arch == 'nexus'
- name: Build prjoxide - name: Build prjoxide

View File

@ -0,0 +1,132 @@
name: FPGA interchange CI tests
on: [push, pull_request]
jobs:
Build-yosys:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- uses: actions/setup-python@v2
- name: Install
run: |
sudo apt-get update
sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev clang bison flex swig locales libtinfo-dev
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: Get yosys
run: |
git clone https://github.com/YosysHQ/yosys.git
cd yosys
echo "YOSYS_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
- name: Cache yosys installation
uses: actions/cache@v2
id: cache-yosys
with:
path: .yosys
key: cache-yosys-${{ env.YOSYS_SHA }}
- name: Build yosys
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
source ./.github/ci/build_interchange.sh
build_yosys
if: steps.cache-yosys.outputs.cache-hit != 'true'
Build-nextpnr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- uses: actions/setup-python@v2
- name: Install
run: |
sudo apt-get update
sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev clang bison flex swig
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: Execute build nextpnr
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
source ./.github/ci/build_interchange.sh
build_nextpnr
Run-Tests:
runs-on: ubuntu-latest
needs: [Build-yosys, Build-nextpnr]
strategy:
# Don't terminate jobs when one fails. This is important when
# debugging CI failures.
fail-fast: false
matrix:
device: [xc7a35t, xc7a100t, xc7a200t, xc7z010, LIFCL-17, LIFCL-40]
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- uses: actions/setup-python@v2
- name: Install
run: |
sudo apt-get update
sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev clang bison flex swig
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: Get yosys
run: |
git clone https://github.com/YosysHQ/yosys.git
cd yosys
echo "YOSYS_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
- name: Cache yosys installation
uses: actions/cache@v2
id: cache-yosys
with:
path: .yosys
key: cache-yosys-${{ env.YOSYS_SHA }}
- name: Build yosys
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
source ./.github/ci/build_interchange.sh
build_yosys
if: steps.cache-yosys.outputs.cache-hit != 'true'
- name: Execute build interchange script
env:
RAPIDWRIGHT_PATH: ${{ github.workspace }}/RapidWright
PYTHON_INTERCHANGE_PATH: ${{ github.workspace }}/python-fpga-interchange
PYTHON_INTERCHANGE_TAG: v0.0.20
PRJOXIDE_REVISION: 318331f8b30c2e2a31cc41d51f104b671e180a8a
DEVICE: ${{ matrix.device }}
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
source ./.github/ci/build_interchange.sh
build_nextpnr && get_dependencies
- name: Run tests
env:
DEVICE: ${{ matrix.device }}
run: |
export PATH="$GITHUB_WORKSPACE/.yosys/usr/local/bin:$PATH"
which yosys
cd build
make all-$DEVICE-tests -j`nproc`

4
.gitignore vendored
View File

@ -1,3 +1,4 @@
/generated/
/objs/ /objs/
/nextpnr-generic* /nextpnr-generic*
/nextpnr-ice40* /nextpnr-ice40*
@ -6,8 +7,6 @@
/nextpnr-fpga_interchange* /nextpnr-fpga_interchange*
/nextpnr-gowin* /nextpnr-gowin*
/nextpnr-machxo2* /nextpnr-machxo2*
/nextpnr-himbaechel*
.cache
cmake-build-*/ cmake-build-*/
Makefile Makefile
cmake_install.cmake cmake_install.cmake
@ -28,7 +27,6 @@ a.out
*.il *.il
/generic/examples/blinky.png /generic/examples/blinky.png
build/ build/
share/
*.asc *.asc
*.bin *.bin
/Testing/* /Testing/*

9
.gitmodules vendored
View File

@ -1,9 +1,6 @@
[submodule "tests"] [submodule "tests"]
path = tests path = tests
url = https://github.com/YosysHQ/nextpnr-tests url = https://github.com/YosysHQ/nextpnr-tests
[submodule "himbaechel/uarch/xilinx/meta"] [submodule "fpga-interchange-schema"]
path = himbaechel/uarch/xilinx/meta path = 3rdparty/fpga-interchange-schema
url = https://github.com/gatecat/nextpnr-xilinx-meta url = https://github.com/SymbiFlow/fpga-interchange-schema.git
[submodule "3rdparty/corrosion"]
path = 3rdparty/corrosion
url = https://github.com/corrosion-rs/corrosion

View File

@ -1,5 +1,4 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.13) CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
PROJECT(QtPropertyBrowser) PROJECT(QtPropertyBrowser)
##################### Look for required libraries ###################### ##################### Look for required libraries ######################

1
3rdparty/corrosion vendored

@ -1 +0,0 @@
Subproject commit be76480232216a64f65e3b1d9794d68cbac6c690

1
3rdparty/fpga-interchange-schema vendored Submodule

@ -0,0 +1 @@
Subproject commit 6b2973788692be86c4a8b2cff1353e603e5857a3

View File

@ -1004,7 +1004,7 @@ void StackLowerThanAddress(const void* ptr, bool* result) {
// Make sure AddressSanitizer does not tamper with the stack here. // Make sure AddressSanitizer does not tamper with the stack here.
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
bool StackGrowsDown() { bool StackGrowsDown() {
int dummy = 0; int dummy;
bool result; bool result;
StackLowerThanAddress(&dummy, &result); StackLowerThanAddress(&dummy, &result);
return result; return result;

View File

@ -13,7 +13,6 @@
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#define NOMINMAX
#include <windows.h> #include <windows.h>
#include <tchar.h> #include <tchar.h>

View File

@ -7,7 +7,6 @@
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1 #define WIN32_LEAN_AND_MEAN 1
#define NOMINMAX
#include <windows.h> #include <windows.h>
static HMODULE libgl; static HMODULE libgl;

View File

@ -57,7 +57,6 @@ extern "C" {
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
#define WIN32_LEAN_AND_MEAN 1 #define WIN32_LEAN_AND_MEAN 1
#define NOMINMAX
#include <windows.h> #include <windows.h>
#endif #endif

View File

@ -85,8 +85,7 @@ extern "C" {
// example to allow applications to correctly declare a GL_ARB_debug_output // example to allow applications to correctly declare a GL_ARB_debug_output
// callback) but windows.h assumes no one will define APIENTRY before it does // callback) but windows.h assumes no one will define APIENTRY before it does
#undef APIENTRY #undef APIENTRY
#define NOMINMAX #include <windows.h>
#include <windows.h>
#elif defined(GLFW_EXPOSE_NATIVE_COCOA) #elif defined(GLFW_EXPOSE_NATIVE_COCOA)
#include <ApplicationServices/ApplicationServices.h> #include <ApplicationServices/ApplicationServices.h>
#if defined(__OBJC__) #if defined(__OBJC__)

View File

@ -8723,10 +8723,8 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#ifndef __MINGW32__ #ifndef __MINGW32__
#define NOMINMAX #include <Windows.h>
#include <windows.h>
#else #else
#define NOMINMAX
#include <windows.h> #include <windows.h>
#endif #endif
#endif #endif

View File

@ -1,5 +0,0 @@
add_library(json11 STATIC
json11.cpp
json11.hpp
)
target_include_directories(json11 PUBLIC .)

View File

@ -23,7 +23,6 @@
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <cstdlib> #include <cstdlib>
#include <cstdint>
#include <cstdio> #include <cstdio>
#include <climits> #include <climits>
#include <cerrno> #include <cerrno>
@ -153,7 +152,7 @@ protected:
// Constructors // Constructors
explicit Value(const T &value) : m_value(value) {} explicit Value(const T &value) : m_value(value) {}
explicit Value(T &&value) : m_value(std::move(value)) {} explicit Value(T &&value) : m_value(move(value)) {}
// Get type tag // Get type tag
Json::Type type() const override { Json::Type type() const override {
@ -200,7 +199,7 @@ class JsonString final : public Value<Json::STRING, string> {
const string &string_value() const override { return m_value; } const string &string_value() const override { return m_value; }
public: public:
explicit JsonString(const string &value) : Value(value) {} explicit JsonString(const string &value) : Value(value) {}
explicit JsonString(string &&value) : Value(std::move(value)) {} explicit JsonString(string &&value) : Value(move(value)) {}
}; };
class JsonArray final : public Value<Json::ARRAY, Json::array> { class JsonArray final : public Value<Json::ARRAY, Json::array> {
@ -208,7 +207,7 @@ class JsonArray final : public Value<Json::ARRAY, Json::array> {
const Json & operator[](size_t i) const override; const Json & operator[](size_t i) const override;
public: public:
explicit JsonArray(const Json::array &value) : Value(value) {} explicit JsonArray(const Json::array &value) : Value(value) {}
explicit JsonArray(Json::array &&value) : Value(std::move(value)) {} explicit JsonArray(Json::array &&value) : Value(move(value)) {}
}; };
class JsonObject final : public Value<Json::OBJECT, Json::object> { class JsonObject final : public Value<Json::OBJECT, Json::object> {
@ -216,7 +215,7 @@ class JsonObject final : public Value<Json::OBJECT, Json::object> {
const Json & operator[](const string &key) const override; const Json & operator[](const string &key) const override;
public: public:
explicit JsonObject(const Json::object &value) : Value(value) {} explicit JsonObject(const Json::object &value) : Value(value) {}
explicit JsonObject(Json::object &&value) : Value(std::move(value)) {} explicit JsonObject(Json::object &&value) : Value(move(value)) {}
}; };
class JsonNull final : public Value<Json::NUL, NullStruct> { class JsonNull final : public Value<Json::NUL, NullStruct> {
@ -258,12 +257,12 @@ Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {} Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {} Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(std::move(value))) {} Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {} Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {} Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(std::move(values))) {} Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {} Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(std::move(values))) {} Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
/* * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * *
* Accessors * Accessors
@ -361,7 +360,7 @@ struct JsonParser final {
* Mark this parse as failed. * Mark this parse as failed.
*/ */
Json fail(string &&msg) { Json fail(string &&msg) {
return fail(std::move(msg), Json()); return fail(move(msg), Json());
} }
template <typename T> template <typename T>

View File

@ -1,9 +0,0 @@
add_library(oourafft INTERFACE)
target_include_directories(oourafft INTERFACE .)
target_sources(oourafft PUBLIC
fftsg.cc
fftsg.h
fftsg2d.cc
)

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
#ifndef FFTSG_H
#define FFTSG_H
#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
//
// The following FFT library came from
// http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html
//
//
/// 1D FFT ////////////////////////////////////////////////////////////////
void ddct(int n, int isgn, float *a, int *ip, float *w);
void ddst(int n, int isgn, float *a, int *ip, float *w);
/// 2D FFT ////////////////////////////////////////////////////////////////
void ddct2d(int n1, int n2, int isgn, float **a, float *t, int *ip, float *w);
void ddsct2d(int n1, int n2, int isgn, float **a, float *t, int *ip, float *w);
void ddcst2d(int n1, int n2, int isgn, float **a, float *t, int *ip, float *w);
NEXTPNR_NAMESPACE_END
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,167 +0,0 @@
General Purpose FFT (Fast Fourier/Cosine/Sine Transform) Package
Description:
A package to calculate Discrete Fourier/Cosine/Sine Transforms of
1-dimensional sequences of length 2^N.
Files:
fft4g.c : FFT Package in C - Fast Version I (radix 4,2)
fft4g.f : FFT Package in Fortran - Fast Version I (radix 4,2)
fft4g_h.c : FFT Package in C - Simple Version I (radix 4,2)
fft8g.c : FFT Package in C - Fast Version II (radix 8,4,2)
fft8g.f : FFT Package in Fortran - Fast Version II (radix 8,4,2)
fft8g_h.c : FFT Package in C - Simple Version II (radix 8,4,2)
fftsg.c : FFT Package in C - Fast Version III (Split-Radix)
fftsg.f : FFT Package in Fortran - Fast Version III (Split-Radix)
fftsg_h.c : FFT Package in C - Simple Version III (Split-Radix)
readme.txt : Readme File
sample1/ : Test Directory
Makefile : for gcc, cc
Makefile.f77: for Fortran
testxg.c : Test Program for "fft*g.c"
testxg.f : Test Program for "fft*g.f"
testxg_h.c : Test Program for "fft*g_h.c"
sample2/ : Benchmark Directory
Makefile : for gcc, cc
Makefile.pth: POSIX Thread version
pi_fft.c : PI(= 3.1415926535897932384626...) Calculation Program
for a Benchmark Test for "fft*g.c"
Difference of the Files:
C and Fortran versions are equal and
the same routines are in each version.
"fft4g*.*" are optimized for most machines.
"fft8g*.*" are fast on the UltraSPARC.
"fftsg*.*" are optimized for the machines that
have the multi-level (L1,L2,etc) cache.
The simple versions "fft*g_h.c" use no work area, but
the fast versions "fft*g.*" use work areas.
The fast versions "fft*g.*" have the same specification.
Routines in the Package:
cdft: Complex Discrete Fourier Transform
rdft: Real Discrete Fourier Transform
ddct: Discrete Cosine Transform
ddst: Discrete Sine Transform
dfct: Cosine Transform of RDFT (Real Symmetric DFT)
dfst: Sine Transform of RDFT (Real Anti-symmetric DFT)
Usage:
Please refer to the comments in the "fft**.*" file which
you want to use. Brief explanations are in the block
comments of each package. The examples are also given in
the test programs.
Method:
-------- cdft --------
fft4g*.*, fft8g*.*:
A method of in-place, radix 2^M, Sande-Tukey (decimation in
frequency). Index of the butterfly loop is in bit
reverse order to keep continuous memory access.
fftsg*.*:
A method of in-place, Split-Radix, recursive fast
algorithm.
-------- rdft --------
A method with a following butterfly operation appended to "cdft".
In forward transform :
A[k] = sum_j=0^n-1 a[j]*W(n)^(j*k), 0<=k<=n/2,
W(n) = exp(2*pi*i/n),
this routine makes an array x[] :
x[j] = a[2*j] + i*a[2*j+1], 0<=j<n/2
and calls "cdft" of length n/2 :
X[k] = sum_j=0^n/2-1 x[j] * W(n/2)^(j*k), 0<=k<n.
The result A[k] are :
A[k] = X[k] - (1+i*W(n)^k)/2 * (X[k]-conjg(X[n/2-k])),
A[n/2-k] = X[n/2-k] +
conjg((1+i*W(n)^k)/2 * (X[k]-conjg(X[n/2-k]))),
0<=k<=n/2
(notes: conjg() is a complex conjugate, X[n/2]=X[0]).
-------- ddct --------
A method with a following butterfly operation appended to "rdft".
In backward transform :
C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k<n,
this routine makes an array r[] :
r[0] = a[0],
r[j] = Re((a[j] - i*a[n-j]) * W(4*n)^j*(1+i)/2),
r[n-j] = Im((a[j] - i*a[n-j]) * W(4*n)^j*(1+i)/2),
0<j<=n/2
and calls "rdft" of length n :
A[k] = sum_j=0^n-1 r[j]*W(n)^(j*k), 0<=k<=n/2,
W(n) = exp(2*pi*i/n).
The result C[k] are :
C[2*k] = Re(A[k] * (1-i)),
C[2*k-1] = -Im(A[k] * (1-i)).
-------- ddst --------
A method with a following butterfly operation appended to "rdft".
In backward transform :
S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k<n,
this routine makes an array r[] :
r[0] = a[0],
r[j] = Im((a[n-j] - i*a[j]) * W(4*n)^j*(1+i)/2),
r[n-j] = Re((a[n-j] - i*a[j]) * W(4*n)^j*(1+i)/2),
0<j<=n/2
and calls "rdft" of length n :
A[k] = sum_j=0^n-1 r[j]*W(n)^(j*k), 0<=k<=n/2,
W(n) = exp(2*pi*i/n).
The result S[k] are :
S[2*k] = Re(A[k] * (1+i)),
S[2*k-1] = -Im(A[k] * (1+i)).
-------- dfct --------
A method to split into "dfct" and "ddct" of half length.
The transform :
C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n
is divided into :
C[2*k] = sum'_j=0^n/2 (a[j]+a[n-j])*cos(pi*j*k/(n/2)),
C[2*k+1] = sum_j=0^n/2-1 (a[j]-a[n-j])*cos(pi*j*(k+1/2)/(n/2))
(sum' is a summation whose last term multiplies 1/2).
This routine uses "ddct" recursively.
To keep the in-place operation, the data in fft*g_h.*
are sorted in bit reversal order.
-------- dfst --------
A method to split into "dfst" and "ddst" of half length.
The transform :
S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0<k<n
is divided into :
S[2*k] = sum_j=1^n/2-1 (a[j]-a[n-j])*sin(pi*j*k/(n/2)),
S[2*k+1] = sum'_j=1^n/2 (a[j]+a[n-j])*sin(pi*j*(k+1/2)/(n/2))
(sum' is a summation whose last term multiplies 1/2).
This routine uses "ddst" recursively.
To keep the in-place operation, the data in fft*g_h.*
are sorted in bit reversal order.
Reference:
* Masatake MORI, Makoto NATORI, Tatuo TORII: Suchikeisan,
Iwanamikouzajyouhoukagaku18, Iwanami, 1982 (Japanese)
* Henri J. Nussbaumer: Fast Fourier Transform and Convolution
Algorithms, Springer Verlag, 1982
* C. S. Burrus, Notes on the FFT (with large FFT paper list)
http://www-dsp.rice.edu/research/fft/fftnote.asc
Copyright:
Copyright(C) 1996-2001 Takuya OOURA
email: ooura@mmm.t.u-tokyo.ac.jp
download: http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html
You may use, copy, modify this code for any purpose and
without fee. You may distribute this ORIGINAL package.
History:
...
Dec. 1995 : Edit the General Purpose FFT
Mar. 1996 : Change the specification
Jun. 1996 : Change the method of trigonometric function table
Sep. 1996 : Modify the documents
Feb. 1997 : Change the butterfly loops
Dec. 1997 : Modify the documents
Dec. 1997 : Add "fft4g.*"
Jul. 1998 : Fix some bugs in the documents
Jul. 1998 : Add "fft8g.*" and delete "fft4f.*"
Jul. 1998 : Add a benchmark program "pi_fft.c"
Jul. 1999 : Add a simple version "fft*g_h.c"
Jul. 1999 : Add a Split-Radix FFT package "fftsg*.c"
Sep. 1999 : Reduce the memory operation (minor optimization)
Oct. 1999 : Change the butterfly structure of "fftsg*.c"
Oct. 1999 : Save the code size
Sep. 2001 : Add "fftsg.f"
Sep. 2001 : Add Pthread & Win32thread routines to "fftsg*.c"
Dec. 2006 : Fix a minor bug in "fftsg.f"

View File

@ -1,77 +0,0 @@
General Purpose 2D,3D FFT (Fast Fourier Transform) Package
Files
alloc.c : 2D-array Allocation
alloc.h : 2D-array Allocation
fft4f2d.c : 2D FFT Package in C - Version I (radix 4, 2)
fft4f2d.f : 2D FFT Package in Fortran - Version I (radix 4, 2)
fftsg.c : 1D FFT Package in C - Fast Version (Split-Radix)
fftsg.f : 1D FFT Package in Fortran - Fast Version (Split-Radix)
fftsg2d.c : 2D FFT Package in C - Version II (Split-Radix)
fftsg2d.f : 2D FFT Package in Fortran - Version II (Split-Radix)
fftsg3d.c : 3D FFT Package in C - Version II (Split-Radix)
fftsg3d.f : 3D FFT Package in Fortran - Version II (Split-Radix)
shrtdct.c : 8x8, 16x16 DCT Package
sample2d/
Makefile : for gcc, cc
Makefile.f77: for Fortran
Makefile.pth: Pthread version
fft4f2dt.c : Test Program for "fft4f2d.c"
fft4f2dt.f : Test Program for "fft4f2d.f"
fftsg2dt.c : Test Program for "fftsg2d.c"
fftsg2dt.f : Test Program for "fftsg2d.f"
fftsg3dt.c : Test Program for "fftsg3d.c"
fftsg3dt.f : Test Program for "fftsg3d.f"
shrtdctt.c : Test Program for "shrtdct.c"
Difference of Files
C and Fortran versions are equal and
the same routines are in each version.
---- Difference between "fft4f2d.*" and "fftsg2d.*" ----
"fft4f2d.*" are optimized for the old machines that
don't have the large size CPU cache.
"fftsg2d.*", "fftsg3d.*" use 1D FFT routines in "fftsg.*".
"fftsg2d.*", "fftsg3d.*" are optimized for the machines that
have the multi-level (L1,L2,etc) cache.
Routines in the Package
in fft4f2d.*, fftsg2d.*
cdft2d: 2-dim Complex Discrete Fourier Transform
rdft2d: 2-dim Real Discrete Fourier Transform
ddct2d: 2-dim Discrete Cosine Transform
ddst2d: 2-dim Discrete Sine Transform
rdft2dsort: rdft2d input/output ordering (fftsg2d.*)
in fftsg3d.*
cdft3d: 3-dim Complex Discrete Fourier Transform
rdft3d: 3-dim Real Discrete Fourier Transform
ddct3d: 3-dim Discrete Cosine Transform
ddst3d: 3-dim Discrete Sine Transform
rdft3dsort: rdft3d input/output ordering
in fftsg.*
cdft: 1-dim Complex Discrete Fourier Transform
rdft: 1-dim Real Discrete Fourier Transform
ddct: 1-dim Discrete Cosine Transform
ddst: 1-dim Discrete Sine Transform
dfct: 1-dim Real Symmetric DFT
dfst: 1-dim Real Anti-symmetric DFT
(these routines are called by fftsg2d.*, fftsg3d.*)
in shrtdct.c
ddct8x8s : Normalized 8x8 DCT
ddct16x16s: Normalized 16x16 DCT
(faster than ddct2d())
Usage
Brief explanations are in block comments of each packages.
The examples are given in the test programs.
Copyright
Copyright(C) 1997,2001 Takuya OOURA (email: ooura@kurims.kyoto-u.ac.jp).
You may use, copy, modify this code for any purpose and
without fee. You may distribute this ORIGINAL package.
History
...
Nov. 2001 : Add 3D-FFT routines
Dec. 2006 : Fix a documentation bug in "fftsg3d.*"
Dec. 2006 : Fix a minor bug in "fftsg.f"

View File

@ -1,6 +1,6 @@
version: 1.0.{build} version: 1.0.{build}
image: image:
- Visual Studio 2017 - Visual Studio 2015
test: off test: off
skip_branch_with_pr: true skip_branch_with_pr: true
build: build:
@ -11,13 +11,15 @@ environment:
matrix: matrix:
- PYTHON: 36 - PYTHON: 36
CONFIG: Debug CONFIG: Debug
- PYTHON: 27
CONFIG: Debug
install: install:
- ps: | - ps: |
$env:CMAKE_GENERATOR = "Visual Studio 15 2017" $env:CMAKE_GENERATOR = "Visual Studio 14 2015"
if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" }
$env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH"
python -W ignore -m pip install --upgrade pip wheel python -W ignore -m pip install --upgrade pip wheel
python -W ignore -m pip install pytest numpy --no-warn-script-location pytest-timeout python -W ignore -m pip install pytest numpy --no-warn-script-location
- ps: | - ps: |
Start-FileDownload 'https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.zip' Start-FileDownload 'https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.zip'
7z x eigen-3.3.7.zip -y > $null 7z x eigen-3.3.7.zip -y > $null

View File

@ -1,38 +0,0 @@
---
# See all possible options and defaults with:
# clang-format --style=llvm --dump-config
BasedOnStyle: LLVM
AccessModifierOffset: -4
AllowShortLambdasOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: All
BreakConstructorInitializers: BeforeColon
ColumnLimit: 99
CommentPragmas: 'NOLINT:.*|^ IWYU pragma:'
IncludeBlocks: Regroup
IndentCaseLabels: true
IndentPPDirectives: AfterHash
IndentWidth: 4
Language: Cpp
SpaceAfterCStyleCast: true
Standard: Cpp11
StatementMacros: ['PyObject_HEAD']
TabWidth: 4
IncludeCategories:
- Regex: '<pybind11/.*'
Priority: -1
- Regex: 'pybind11.h"$'
Priority: 1
- Regex: '^".*/?detail/'
Priority: 1
SortPriority: 2
- Regex: '^"'
Priority: 1
SortPriority: 3
- Regex: '<[[:alnum:]._]+>'
Priority: 4
- Regex: '.*'
Priority: 5
...

View File

@ -1,77 +1,13 @@
FormatStyle: file FormatStyle: file
Checks: | Checks: '
*bugprone*, llvm-namespace-comment,
*performance*, modernize-use-override,
clang-analyzer-optin.cplusplus.VirtualCall, readability-container-size-empty,
clang-analyzer-optin.performance.Padding, modernize-use-using,
cppcoreguidelines-init-variables, modernize-use-equals-default,
cppcoreguidelines-prefer-member-initializer, modernize-use-auto,
cppcoreguidelines-pro-type-static-cast-downcast, modernize-use-emplace,
cppcoreguidelines-slicing, '
google-explicit-constructor,
llvm-namespace-comment,
misc-definitions-in-headers,
misc-misplaced-const,
misc-non-copyable-objects,
misc-static-assert,
misc-throw-by-value-catch-by-reference,
misc-uniqueptr-reset-release,
misc-unused-parameters,
modernize-avoid-bind,
modernize-loop-convert,
modernize-make-shared,
modernize-redundant-void-arg,
modernize-replace-auto-ptr,
modernize-replace-disallow-copy-and-assign-macro,
modernize-replace-random-shuffle,
modernize-shrink-to-fit,
modernize-use-auto,
modernize-use-bool-literals,
modernize-use-default-member-init,
modernize-use-emplace,
modernize-use-equals-default,
modernize-use-equals-delete,
modernize-use-noexcept,
modernize-use-nullptr,
modernize-use-override,
modernize-use-using,
readability-avoid-const-params-in-decls,
readability-braces-around-statements,
readability-const-return-type,
readability-container-size-empty,
readability-delete-null-pointer,
readability-else-after-return,
readability-implicit-bool-conversion,
readability-inconsistent-declaration-parameter-name,
readability-make-member-function-const,
readability-misplaced-array-index,
readability-non-const-parameter,
readability-qualified-auto,
readability-redundant-function-ptr-dereference,
readability-redundant-smartptr-get,
readability-redundant-string-cstr,
readability-simplify-subscript-expr,
readability-static-accessed-through-instance,
readability-static-definition-in-anonymous-namespace,
readability-string-compare,
readability-suspicious-call-argument,
readability-uniqueptr-delete-release,
-bugprone-easily-swappable-parameters,
-bugprone-exception-escape,
-bugprone-reserved-identifier,
-bugprone-unused-raii,
CheckOptions:
- key: modernize-use-equals-default.IgnoreMacros
value: false
- key: performance-for-range-copy.WarnOnAllAutoCopies
value: true
- key: performance-inefficient-string-concatenation.StrictMode
value: true
- key: performance-unnecessary-value-param.AllowedTypes
value: 'exception_ptr$;'
- key: readability-implicit-bool-conversion.AllowPointerConditions
value: true
HeaderFilterRegex: 'pybind11/.*h' HeaderFilterRegex: 'pybind11/.*h'

View File

@ -1,24 +0,0 @@
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t>
template <typename ThisT>
auto &this_ = static_cast<ThisT &>(*this);
if (load_impl<ThisT>(temp, false)) {
ssize_t nd = 0;
auto trivial = broadcast(buffers, nd, shape);
auto ndim = (size_t) nd;
int nd;
ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; }
using op = op_impl<id, ot, Base, L_type, R_type>;
template <op_id id, op_type ot, typename L, typename R>
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
class_ &def_cast(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
struct IntStruct {
explicit IntStruct(int v) : value(v){};
~IntStruct() { value = -value; }
IntStruct(const IntStruct &) = default;
IntStruct &operator=(const IntStruct &) = default;
py::class_<IntStruct>(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); }));
py::implicitly_convertible<int, IntStruct>();
m.def("test", [](int expected, const IntStruct &in) {
[](int expected, const IntStruct &in) {

View File

@ -1 +0,0 @@
docs/*.svg binary

View File

@ -1,9 +0,0 @@
*.cmake @henryiii
CMakeLists.txt @henryiii
*.yml @henryiii
*.yaml @henryiii
/tools/ @henryiii
/pybind11/ @henryiii
noxfile.py @henryiii
.clang-format @henryiii
.clang-tidy @henryiii

View File

@ -53,33 +53,6 @@ derivative works thereof, in binary and source code form.
## Development of pybind11 ## Development of pybind11
### Quick setup
To setup a quick development environment, use [`nox`](https://nox.thea.codes).
This will allow you to do some common tasks with minimal setup effort, but will
take more time to run and be less flexible than a full development environment.
If you use [`pipx run nox`](https://pipx.pypa.io), you don't even need to
install `nox`. Examples:
```bash
# List all available sessions
nox -l
# Run linters
nox -s lint
# Run tests on Python 3.9
nox -s tests-3.9
# Build and preview docs
nox -s docs -- serve
# Build SDists and wheels
nox -s build
```
### Full setup
To setup an ideal development environment, run the following commands on a To setup an ideal development environment, run the following commands on a
system with CMake 3.14+: system with CMake 3.14+:
@ -93,10 +66,11 @@ cmake --build build -j4
Tips: Tips:
* You can use `virtualenv` (faster, from PyPI) instead of `venv`. * You can use `virtualenv` (from PyPI) instead of `venv` (which is Python 3
only).
* You can select any name for your environment folder; if it contains "env" it * You can select any name for your environment folder; if it contains "env" it
will be ignored by git. will be ignored by git.
* If you don't have CMake 3.14+, just add "cmake" to the pip install command. * If you dont 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+ * You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+
* In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`. * In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`.
FindPython uses `-DPython_ROOT_DIR=/path/to` or FindPython uses `-DPython_ROOT_DIR=/path/to` or
@ -104,7 +78,7 @@ Tips:
### Configuration options ### Configuration options
In CMake, configuration options are given with "-D". Options are stored in the In CMake, configuration options are given with “-D”. Options are stored in the
build directory, in the `CMakeCache.txt` file, so they are remembered for each build directory, in the `CMakeCache.txt` file, so they are remembered for each
build directory. Two selections are special - the generator, given with `-G`, build directory. Two selections are special - the generator, given with `-G`,
and the compiler, which is selected based on environment variables `CXX` and and the compiler, which is selected based on environment variables `CXX` and
@ -114,12 +88,12 @@ after the initial run.
The valid options are: The valid options are:
* `-DCMAKE_BUILD_TYPE`: Release, Debug, MinSizeRel, RelWithDebInfo * `-DCMAKE_BUILD_TYPE`: Release, Debug, MinSizeRel, RelWithDebInfo
* `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+'s FindPython instead of the * `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+s FindPython instead of the
classic, deprecated, custom FindPythonLibs classic, deprecated, custom FindPythonLibs
* `-DPYBIND11_NOPYTHON=ON`: Disable all Python searching (disables tests) * `-DPYBIND11_NOPYTHON=ON`: Disable all Python searching (disables tests)
* `-DBUILD_TESTING=ON`: Enable the tests * `-DBUILD_TESTING=ON`: Enable the tests
* `-DDOWNLOAD_CATCH=ON`: Download catch to build the C++ tests * `-DDOWNLOAD_CATCH=ON`: Download catch to build the C++ tests
* `-DDOWNLOAD_EIGEN=ON`: Download Eigen for the NumPy tests * `-DOWNLOAD_EIGEN=ON`: Download Eigen for the NumPy tests
* `-DPYBIND11_INSTALL=ON/OFF`: Enable the install target (on by default for the * `-DPYBIND11_INSTALL=ON/OFF`: Enable the install target (on by default for the
master project) master project)
* `-DUSE_PYTHON_INSTALL_DIR=ON`: Try to install into the python dir * `-DUSE_PYTHON_INSTALL_DIR=ON`: Try to install into the python dir
@ -135,7 +109,7 @@ The valid options are:
* Use `-G` and the name of a generator to use something different. `cmake * Use `-G` and the name of a generator to use something different. `cmake
--help` lists the generators available. --help` lists the generators available.
- On Unix, setting `CMAKE_GENERATER=Ninja` in your environment will give - On Unix, setting `CMAKE_GENERATER=Ninja` in your environment will give
you automatic multithreading on all your CMake projects! you automatic mulithreading on all your CMake projects!
* Open the `CMakeLists.txt` with QtCreator to generate for that IDE. * Open the `CMakeLists.txt` with QtCreator to generate for that IDE.
* You can use `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to generate the `.json` file * You can use `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to generate the `.json` file
that some tools expect. that some tools expect.
@ -152,26 +126,13 @@ cmake --build build --target check
`--target` can be spelled `-t` in CMake 3.15+. You can also run individual `--target` can be spelled `-t` in CMake 3.15+. You can also run individual
tests with these targets: tests with these targets:
* `pytest`: Python tests only, using the * `pytest`: Python tests only
[pytest](https://docs.pytest.org/en/stable/) framework
* `cpptest`: C++ tests only * `cpptest`: C++ tests only
* `test_cmake_build`: Install / subdirectory tests * `test_cmake_build`: Install / subdirectory tests
If you want to build just a subset of tests, use If you want to build just a subset of tests, use
`-DPYBIND11_TEST_OVERRIDE="test_callbacks;test_pickling"`. If this is `-DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp"`. If this is
empty, all tests will be built. Tests are specified without an extension if they need both a .py and empty, all tests will be built.
.cpp file.
You may also pass flags to the `pytest` target by editing `tests/pytest.ini` or
by using the `PYTEST_ADDOPTS` environment variable
(see [`pytest` docs](https://docs.pytest.org/en/2.7.3/customize.html#adding-default-options)). As an example:
```bash
env PYTEST_ADDOPTS="--capture=no --exitfirst" \
cmake --build build --target pytest
# Or using abbreviated flags
env PYTEST_ADDOPTS="-s -x" cmake --build build --target pytest
```
### Formatting ### Formatting
@ -203,46 +164,18 @@ name, pre-commit):
pre-commit install pre-commit install
``` ```
### Clang-Format
As of v2.6.2, pybind11 ships with a [`clang-format`][clang-format]
configuration file at the top level of the repo (the filename is
`.clang-format`). Currently, formatting is NOT applied automatically, but
manually using `clang-format` for newly developed files is highly encouraged.
To check if a file needs formatting:
```bash
clang-format -style=file --dry-run some.cpp
```
The output will show things to be fixed, if any. To actually format the file:
```bash
clang-format -style=file -i some.cpp
```
Note that the `-style-file` option searches the parent directories for the
`.clang-format` file, i.e. the commands above can be run in any subdirectory
of the pybind11 repo.
### Clang-Tidy ### Clang-Tidy
[`clang-tidy`][clang-tidy] performs deeper static code analyses and is To run Clang tidy, the following recipe should work. Files will be modified in
more complex to run, compared to `clang-format`, but support for `clang-tidy` place, so you can use git to monitor the changes.
is built into the pybind11 CMake configuration. To run `clang-tidy`, the
following recipe should work. Run the `docker` command from the top-level
directory inside your pybind11 git clone. Files will be modified in place,
so you can use git to monitor the changes.
```bash ```bash
docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:15-bullseye docker run --rm -v $PWD:/pybind11 -it silkeh/clang:10
apt-get update && apt-get install -y git python3-dev python3-pytest apt-get update && apt-get install python3-dev python3-pytest
cmake -S /mounted_pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17 cmake -S pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-fix"
cmake --build build -j 2 cmake --build build
``` ```
You can add `--fix` to the options list if you want.
### Include what you use ### Include what you use
To run include what you use, install (`brew install include-what-you-use` on To run include what you use, install (`brew install include-what-you-use` on
@ -253,12 +186,12 @@ cmake -S . -B build-iwyu -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=$(which include-what-y
cmake --build build cmake --build build
``` ```
The report is sent to stderr; you can pipe it into a file if you wish. The report is sent to stderr; you can pip it into a file if you wish.
### Build recipes ### Build recipes
This builds with the Intel compiler (assuming it is in your path, along with a This builds with the Intel compiler (assuming it is in your path, along with a
recent CMake and Python): recent CMake and Python 3):
```bash ```bash
python3 -m venv venv python3 -m venv venv
@ -380,8 +313,6 @@ if you really want to.
[pre-commit]: https://pre-commit.com [pre-commit]: https://pre-commit.com
[clang-format]: https://clang.llvm.org/docs/ClangFormat.html
[clang-tidy]: https://clang.llvm.org/extra/clang-tidy/
[pybind11.readthedocs.org]: http://pybind11.readthedocs.org/en/latest [pybind11.readthedocs.org]: http://pybind11.readthedocs.org/en/latest
[issue tracker]: https://github.com/pybind/pybind11/issues [issue tracker]: https://github.com/pybind/pybind11/issues
[gitter]: https://gitter.im/pybind/Lobby [gitter]: https://gitter.im/pybind/Lobby

View File

@ -0,0 +1,28 @@
---
name: Bug Report
about: File an issue about a bug
title: "[BUG] "
---
Make sure you've completed the following steps before submitting your issue -- thank you!
1. Make sure you've read the [documentation][]. Your issue may be addressed there.
2. Search the [issue tracker][] to verify that this hasn't already been reported. +1 or comment there if it has.
3. Consider asking first in the [Gitter chat room][].
4. Include a self-contained and minimal piece of code that reproduces the problem. If that's not possible, try to make the description as clear as possible.
a. If possible, make a PR with a new, failing test to give us a starting point to work on!
[documentation]: https://pybind11.readthedocs.io
[issue tracker]: https://github.com/pybind/pybind11/issues
[Gitter chat room]: https://gitter.im/pybind/Lobby
*After reading, remove this checklist and the template text in parentheses below.*
## Issue description
(Provide a short description, state the expected behavior and what actually happens.)
## Reproducible example code
(The code should be minimal, have no external dependencies, isolate the function(s) that cause breakage. Submit matched and complete C++ and Python snippets that can be easily compiled and run to diagnose the issue.)

View File

@ -1,61 +0,0 @@
name: Bug Report
description: File an issue about a bug
title: "[BUG]: "
labels: [triage]
body:
- type: markdown
attributes:
value: |
Please do your best to make the issue as easy to act on as possible, and only submit here if there is clearly a problem with pybind11 (ask first if unsure). **Note that a reproducer in a PR is much more likely to get immediate attention.**
- type: checkboxes
id: steps
attributes:
label: Required prerequisites
description: Make sure you've completed the following steps before submitting your issue -- thank you!
options:
- label: Make sure you've read the [documentation](https://pybind11.readthedocs.io). Your issue may be addressed there.
required: true
- label: Search the [issue tracker](https://github.com/pybind/pybind11/issues) and [Discussions](https:/pybind/pybind11/discussions) to verify that this hasn't already been reported. +1 or comment there if it has.
required: true
- label: Consider asking first in the [Gitter chat room](https://gitter.im/pybind/Lobby) or in a [Discussion](https:/pybind/pybind11/discussions/new).
required: false
- type: input
id: version
attributes:
label: What version (or hash if on master) of pybind11 are you using?
validations:
required: true
- type: textarea
id: description
attributes:
label: Problem description
placeholder: >-
Provide a short description, state the expected behavior and what
actually happens. Include relevant information like what version of
pybind11 you are using, what system you are on, and any useful commands
/ output.
validations:
required: true
- type: textarea
id: code
attributes:
label: Reproducible example code
placeholder: >-
The code should be minimal, have no external dependencies, isolate the
function(s) that cause breakage. Submit matched and complete C++ and
Python snippets that can be easily compiled and run to diagnose the
issue. — Note that a reproducer in a PR is much more likely to get
immediate attention: failing tests in the pybind11 CI are the best
starting point for working out fixes.
render: text
- type: input
id: regression
attributes:
label: Is this a regression? Put the last known working version here if it is.
description: Put the last known working version here if this is a regression.
value: Not a regression

View File

@ -1,8 +1,5 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: Ask a question
url: https://github.com/pybind/pybind11/discussions/new
about: Please ask and answer questions here, or propose new ideas.
- name: Gitter room - name: Gitter room
url: https://gitter.im/pybind/Lobby url: https://gitter.im/pybind/Lobby
about: A room for discussing pybind11 with an active community about: A room for discussing pybind11 with an active community

View File

@ -0,0 +1,16 @@
---
name: Feature Request
about: File an issue about adding a feature
title: "[FEAT] "
---
Make sure you've completed the following steps before submitting your issue -- thank you!
1. Check if your feature has already been mentioned / rejected / planned in other issues.
2. If those resources didn't help, consider asking in the [Gitter chat room][] to see if this is interesting / useful to a larger audience and possible to implement reasonably,
4. If you have a useful feature that passes the previous items (or not suitable for chat), please fill in the details below.
[Gitter chat room]: https://gitter.im/pybind/Lobby
*After reading, remove this checklist.*

View File

@ -0,0 +1,21 @@
---
name: Question
about: File an issue about unexplained behavior
title: "[QUESTION] "
---
If you have a question, please check the following first:
1. Check if your question has already been answered in the [FAQ][] section.
2. Make sure you've read the [documentation][]. Your issue may be addressed there.
3. If those resources didn't help and you only have a short question (not a bug report), consider asking in the [Gitter chat room][]
4. Search the [issue tracker][], including the closed issues, to see if your question has already been asked/answered. +1 or comment if it has been asked but has no answer.
5. If you have a more complex question which is not answered in the previous items (or not suitable for chat), please fill in the details below.
6. Include a self-contained and minimal piece of code that illustrates your question. If that's not possible, try to make the description as clear as possible.
[FAQ]: http://pybind11.readthedocs.io/en/latest/faq.html
[documentation]: https://pybind11.readthedocs.io
[issue tracker]: https://github.com/pybind/pybind11/issues
[Gitter chat room]: https://gitter.im/pybind/Lobby
*After reading, remove this checklist.*

View File

@ -4,12 +4,8 @@ updates:
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "daily"
groups:
actions:
patterns:
- "*"
ignore: ignore:
- dependency-name: actions/checkout # Offical actions have moving tags like v1
versions: # that are used, so they don't need updates here
- "<5" - dependency-name: "actions/*"

View File

@ -1,7 +1,3 @@
<!--
Title (above): please place [branch_name] at the beginning if you are targeting a branch other than master. *Do not target stable*.
It is recommended to use conventional commit format, see conventionalcommits.org, but not required.
-->
## Description ## Description
<!-- Include relevant issues or PRs here, describe what changed and why --> <!-- Include relevant issues or PRs here, describe what changed and why -->
@ -9,8 +5,7 @@ It is recommended to use conventional commit format, see conventionalcommits.org
## Suggested changelog entry: ## Suggested changelog entry:
<!-- Fill in the below block with the expected RestructuredText entry. Delete if no entry needed; <!-- fill in the below block with the expected RestructuredText entry (delete if no entry needed) -->
but do not delete header or rst block if an entry is needed! Will be collected via a script. -->
```rst ```rst

File diff suppressed because it is too large Load Diff

View File

@ -9,14 +9,6 @@ on:
- stable - stable
- v* - v*
permissions:
contents: read
env:
PIP_BREAK_SYSTEM_PACKAGES: 1
# For cmake:
VERBOSE: 1
jobs: jobs:
# This tests various versions of CMake in various combinations, to make sure # This tests various versions of CMake in various combinations, to make sure
# the configure step passes. # the configure step passes.
@ -24,35 +16,35 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
runs-on: [ubuntu-20.04, macos-latest, windows-latest] runs-on: [ubuntu-latest, macos-latest, windows-latest]
arch: [x64] arch: [x64]
cmake: ["3.26"] cmake: [3.18]
include: include:
- runs-on: ubuntu-20.04 - runs-on: ubuntu-latest
arch: x64 arch: x64
cmake: "3.5" cmake: 3.4
- runs-on: ubuntu-20.04
arch: x64
cmake: "3.27"
- runs-on: macos-latest - runs-on: macos-latest
arch: x64 arch: x64
cmake: "3.7" cmake: 3.7
- runs-on: windows-2019 - runs-on: windows-2016
arch: x64 # x86 compilers seem to be missing on 2019 image arch: x86
cmake: "3.18" cmake: 3.8
- runs-on: windows-2016
arch: x86
cmake: 3.18
name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }} name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }}
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Setup Python 3.7 - name: Setup Python 3.7
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: 3.7 python-version: 3.7
architecture: ${{ matrix.arch }} architecture: ${{ matrix.arch }}
@ -63,7 +55,7 @@ jobs:
# An action for adding a specific version of CMake: # An action for adding a specific version of CMake:
# https://github.com/jwlawson/actions-setup-cmake # https://github.com/jwlawson/actions-setup-cmake
- name: Setup CMake ${{ matrix.cmake }} - name: Setup CMake ${{ matrix.cmake }}
uses: jwlawson/actions-setup-cmake@v2.0 uses: jwlawson/actions-setup-cmake@v1.3
with: with:
cmake-version: ${{ matrix.cmake }} cmake-version: ${{ matrix.cmake }}

View File

@ -12,49 +12,35 @@ on:
- stable - stable
- "v*" - "v*"
permissions:
contents: read
env:
FORCE_COLOR: 3
# For cmake:
VERBOSE: 1
jobs: jobs:
pre-commit: pre-commit:
name: Format name: Format
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- uses: actions/setup-python@v5 - uses: actions/setup-python@v2
with: - uses: pre-commit/action@v2.0.0
python-version: "3.x"
- name: Add matchers
run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json"
- uses: pre-commit/action@v3.0.1
with: with:
# Slow hooks are marked with manual - slow is okay here, run them too # Slow hooks are marked with manual - slow is okay here, run them too
extra_args: --hook-stage manual --all-files extra_args: --hook-stage manual --all-files
clang-tidy: clang-tidy:
# When making changes here, please also review the "Clang-Tidy" section
# in .github/CONTRIBUTING.md and update as needed.
name: Clang-Tidy name: Clang-Tidy
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: silkeh/clang:15-bullseye container: silkeh/clang:10
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Install requirements - name: Install requirements
run: apt-get update && apt-get install -y git python3-dev python3-pytest run: apt-get update && apt-get install -y python3-dev python3-pytest
- name: Configure - name: Configure
run: > run: >
cmake -S . -B build cmake -S . -B build
-DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color;--warnings-as-errors=*" -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--warnings-as-errors=*"
-DDOWNLOAD_EIGEN=ON -DDOWNLOAD_EIGEN=ON
-DDOWNLOAD_CATCH=ON -DDOWNLOAD_CATCH=ON
-DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD=17
- name: Build - name: Build
run: cmake --build build -j 2 -- --keep-going run: cmake --build build -j 2

View File

@ -3,23 +3,14 @@ on:
pull_request_target: pull_request_target:
types: [closed] types: [closed]
permissions: {}
jobs: jobs:
label: label:
name: Labeler name: Labeler
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps: steps:
- uses: actions/labeler@v4 - uses: actions/labeler@main
if: > if: github.event.pull_request.merged == true
github.event.pull_request.merged == true &&
!startsWith(github.event.pull_request.title, 'chore(deps):') &&
!startsWith(github.event.pull_request.title, 'ci(fix):') &&
!startsWith(github.event.pull_request.title, 'docs(changelog):')
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler_merged.yml configuration-path: .github/labeler_merged.yml

View File

@ -12,32 +12,24 @@ on:
types: types:
- published - published
permissions:
contents: read
env:
PIP_BREAK_SYSTEM_PACKAGES: 1
PIP_ONLY_BINARY: numpy
jobs: jobs:
# This builds the sdists and wheels and makes sure the files are exactly as # This builds the sdists and wheels and makes sure the files are exactly as
# expected. Using Windows and Python 3.6, since that is often the most # expected. Using Windows and Python 2.7, since that is often the most
# challenging matrix element. # challenging matrix element.
test-packaging: test-packaging:
name: 🐍 3.6 • 📦 tests • windows-latest name: 🐍 2.7 • 📦 tests • windows-latest
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Setup 🐍 3.6 - name: Setup 🐍 2.7
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: 3.6 python-version: 2.7
- name: Prepare env - name: Prepare env
run: | run: python -m pip install -r tests/requirements.txt --prefer-binary
python -m pip install -r tests/requirements.txt
- name: Python Packaging tests - name: Python Packaging tests
run: pytest tests/extra_python_package/ run: pytest tests/extra_python_package/
@ -50,16 +42,15 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Setup 🐍 3.8 - name: Setup 🐍 3.8
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: 3.8 python-version: 3.8
- name: Prepare env - name: Prepare env
run: | run: python -m pip install -r tests/requirements.txt build twine --prefer-binary
python -m pip install -r tests/requirements.txt build twine
- name: Python Packaging tests - name: Python Packaging tests
run: pytest tests/extra_python_package/ run: pytest tests/extra_python_package/
@ -73,13 +64,13 @@ jobs:
run: twine check dist/* run: twine check dist/*
- name: Save standard package - name: Save standard package
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: standard name: standard
path: dist/pybind11-* path: dist/pybind11-*
- name: Save global package - name: Save global package
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: global name: global
path: dist/pybind11_global-* path: dist/pybind11_global-*
@ -94,21 +85,19 @@ jobs:
needs: [packaging] needs: [packaging]
steps: steps:
- uses: actions/setup-python@v5 - uses: actions/setup-python@v2
with:
python-version: "3.x"
# Downloads all to directories matching the artifact names # Downloads all to directories matching the artifact names
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v2
- name: Publish standard package - name: Publish standard package
uses: pypa/gh-action-pypi-publish@release/v1 uses: pypa/gh-action-pypi-publish@v1.4.1
with: with:
password: ${{ secrets.pypi_password }} password: ${{ secrets.pypi_password }}
packages-dir: standard/ packages_dir: standard/
- name: Publish global package - name: Publish global package
uses: pypa/gh-action-pypi-publish@release/v1 uses: pypa/gh-action-pypi-publish@v1.4.1
with: with:
password: ${{ secrets.pypi_password_global }} password: ${{ secrets.pypi_password_global }}
packages-dir: global/ packages_dir: global/

View File

@ -1,116 +0,0 @@
name: Upstream
on:
workflow_dispatch:
pull_request:
permissions:
contents: read
concurrency:
group: upstream-${{ github.ref }}
cancel-in-progress: true
env:
PIP_BREAK_SYSTEM_PACKAGES: 1
# For cmake:
VERBOSE: 1
jobs:
standard:
name: "🐍 3.13 latest • ubuntu-latest • x64"
runs-on: ubuntu-latest
# Only runs when the 'python dev' label is selected
if: "contains(github.event.pull_request.labels.*.name, 'python dev')"
steps:
- uses: actions/checkout@v4
- name: Setup Python 3.13
uses: actions/setup-python@v5
with:
python-version: "3.13"
allow-prereleases: true
- name: Setup Boost
run: sudo apt-get install libboost-dev
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v2.0
- name: Run pip installs
run: |
python -m pip install --upgrade pip
python -m pip install -r tests/requirements.txt
- name: Show platform info
run: |
python -m platform
cmake --version
pip list
# First build - C++11 mode and inplace
- name: Configure C++11
run: >
cmake -S . -B build11
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=11
-DCMAKE_BUILD_TYPE=Debug
- name: Build C++11
run: cmake --build build11 -j 2
- name: Python tests C++11
run: cmake --build build11 --target pytest -j 2
- name: C++11 tests
run: cmake --build build11 --target cpptest -j 2
- name: Interface test C++11
run: cmake --build build11 --target test_cmake_build
# Second build - C++17 mode and in a build directory
- name: Configure C++17
run: >
cmake -S . -B build17
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=17
- name: Build C++17
run: cmake --build build17 -j 2
- name: Python tests C++17
run: cmake --build build17 --target pytest
- name: C++17 tests
run: cmake --build build17 --target cpptest
# Third build - C++17 mode with unstable ABI
- name: Configure (unstable ABI)
run: >
cmake -S . -B build17max
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=17
-DPYBIND11_INTERNALS_VERSION=10000000
- name: Build (unstable ABI)
run: cmake --build build17max -j 2
- name: Python tests (unstable ABI)
run: cmake --build build17max --target pytest
- name: Interface test (unstable ABI)
run: cmake --build build17max --target test_cmake_build
# This makes sure the setup_helpers module can build packages using
# setuptools
- name: Setuptools helpers test
run: |
pip install setuptools
pytest tests/extra_setuptools

View File

@ -41,6 +41,3 @@ pybind11Targets.cmake
/.vscode /.vscode
/pybind11/include/* /pybind11/include/*
/pybind11/share/* /pybind11/share/*
/docs/_build/*
.ipynb_checkpoints/
tests/main.cpp

View File

@ -12,144 +12,89 @@
# #
# See https://github.com/pre-commit/pre-commit # See https://github.com/pre-commit/pre-commit
ci:
autoupdate_commit_msg: "chore(deps): update pre-commit hooks"
autofix_commit_msg: "style: pre-commit fixes"
autoupdate_schedule: monthly
# third-party content
exclude: ^tools/JoinPaths.cmake$
repos: repos:
# Clang format the codebase automatically
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v17.0.6"
hooks:
- id: clang-format
types_or: [c++, c, cuda]
# Ruff, the Python auto-correcting linter/formatter written in Rust
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.0
hooks:
- id: ruff
args: ["--fix", "--show-fixes"]
- id: ruff-format
# Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.8.0"
hooks:
- id: mypy
args: []
exclude: ^(tests|docs)/
additional_dependencies:
- markdown-it-py<3 # Drop this together with dropping Python 3.7 support.
- nox
- rich
- types-setuptools
# CMake formatting
- repo: https://github.com/cheshirekow/cmake-format-precommit
rev: "v0.6.13"
hooks:
- id: cmake-format
additional_dependencies: [pyyaml]
types: [file]
files: (\.cmake|CMakeLists.txt)(.in)?$
# Standard hooks # Standard hooks
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v4.5.0" rev: v3.2.0
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
- id: check-case-conflict - id: check-case-conflict
- id: check-docstring-first
- id: check-merge-conflict - id: check-merge-conflict
- id: check-symlinks - id: check-symlinks
- id: check-toml
- id: check-yaml - id: check-yaml
- id: debug-statements - id: debug-statements
- id: end-of-file-fixer - id: end-of-file-fixer
- id: mixed-line-ending - id: mixed-line-ending
- id: requirements-txt-fixer - id: requirements-txt-fixer
- id: trailing-whitespace - id: trailing-whitespace
- id: fix-encoding-pragma
# Also code format the docs # Black, the code formatter, natively supports pre-commit
- repo: https://github.com/asottile/blacken-docs - repo: https://github.com/psf/black
rev: "1.16.0" rev: 20.8b1
hooks: hooks:
- id: blacken-docs - id: black
additional_dependencies: # By default, this ignores pyi files, though black supports them
- black==23.* types: [text]
files: \.pyi?$
# Changes tabs to spaces # Changes tabs to spaces
- repo: https://github.com/Lucas-C/pre-commit-hooks - repo: https://github.com/Lucas-C/pre-commit-hooks
rev: "v1.5.4" rev: v1.1.9
hooks: hooks:
- id: remove-tabs - id: remove-tabs
# Avoid directional quotes # Flake8 also supports pre-commit natively (same author)
- repo: https://github.com/sirosen/texthooks - repo: https://gitlab.com/pycqa/flake8
rev: "0.6.4" rev: 3.8.3
hooks: hooks:
- id: fix-ligatures - id: flake8
- id: fix-smartquotes additional_dependencies: [flake8-bugbear, pep8-naming]
exclude: ^(docs/.*|tools/.*)$
# Checking for common mistakes # CMake formatting
- repo: https://github.com/pre-commit/pygrep-hooks - repo: https://github.com/cheshirekow/cmake-format-precommit
rev: "v1.10.0" rev: v0.6.13
hooks: hooks:
- id: rst-backticks - id: cmake-format
- id: rst-directive-colons additional_dependencies: [pyyaml]
- id: rst-inline-touching-normal types: [file]
files: (\.cmake|CMakeLists.txt)(.in)?$
# Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.790
hooks:
- id: mypy
# The default Python type ignores .pyi files, so let's rerun if detected
types: [text]
files: ^pybind11.*\.pyi?$
# Running per-file misbehaves a bit, so just run on all files, it's fast
pass_filenames: false
# Checks the manifest for missing files (native support) # Checks the manifest for missing files (native support)
- repo: https://github.com/mgedmin/check-manifest - repo: https://github.com/mgedmin/check-manifest
rev: "0.49" rev: "0.43"
hooks: hooks:
- id: check-manifest - id: check-manifest
# This is a slow hook, so only run this if --hook-stage manual is passed # This is a slow hook, so only run this if --hook-stage manual is passed
stages: [manual] stages: [manual]
additional_dependencies: [cmake, ninja] additional_dependencies: [cmake, ninja]
# Check for spelling # The original pybind11 checks for a few C++ style items
# Use tools/codespell_ignore_lines_from_errors.py
# to rebuild .codespell-ignore-lines
- repo: https://github.com/codespell-project/codespell
rev: "v2.2.6"
hooks:
- id: codespell
exclude: ".supp$"
args: ["-x.codespell-ignore-lines", "-Lccompiler"]
# Check for common shell mistakes
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: "v0.9.0.6"
hooks:
- id: shellcheck
# Disallow some common capitalization mistakes
- repo: local - repo: local
hooks: hooks:
- id: disallow-caps - id: disallow-caps
name: Disallow improper capitalization name: Disallow improper capitalization
language: pygrep language: pygrep
entry: PyBind|\bNumpy\b|Cmake|CCache|PyTest entry: PyBind|Numpy|Cmake|CCache
exclude: ^\.pre-commit-config.yaml$ exclude: .pre-commit-config.yaml
# PyLint has native support - not always usable, but works for us - repo: local
- repo: https://github.com/PyCQA/pylint
rev: "v3.0.3"
hooks: hooks:
- id: pylint - id: check-style
files: ^pybind11 name: Classic check-style
language: system
- repo: https://github.com/python-jsonschema/check-jsonschema types:
rev: 0.28.0 - c++
hooks: entry: ./tools/check-style.sh
- id: check-readthedocs
- id: check-github-workflows
- id: check-dependabot

View File

@ -1,20 +1,3 @@
# https://blog.readthedocs.com/migrate-configuration-v2/
version: 2
build:
os: ubuntu-22.04
apt_packages:
- librsvg2-bin
tools:
python: "3.11"
sphinx:
configuration: docs/conf.py
python: python:
install: version: 3
- requirements: docs/requirements.txt requirements_file: docs/requirements.txt
formats:
- pdf

View File

@ -5,30 +5,15 @@
# All rights reserved. Use of this source code is governed by a # All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file. # BSD-style license that can be found in the LICENSE file.
# Propagate this policy (FindPythonInterp removal) so it can be detected later cmake_minimum_required(VERSION 3.4)
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.4...3.18)` syntax does not work with
# The `cmake_minimum_required(VERSION 3.5...3.27)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate # some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround: # the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.27) if(${CMAKE_VERSION} VERSION_LESS 3.18)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else() else()
cmake_policy(VERSION 3.27) cmake_policy(VERSION 3.18)
endif()
if(_pybind11_cmp0148)
cmake_policy(SET CMP0148 ${_pybind11_cmp0148})
unset(_pybind11_cmp0148)
endif()
# Avoid infinite recursion if tests include this as a subdirectory
if(DEFINED PYBIND11_MASTER_PROJECT)
return()
endif() endif()
# Extract project version from source # Extract project version from source
@ -88,71 +73,31 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif() endif()
set(pybind11_system "")
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if(CMAKE_VERSION VERSION_LESS "3.18")
set(_pybind11_findpython_default OFF)
else()
set(_pybind11_findpython_default ON)
endif()
else() else()
set(PYBIND11_MASTER_PROJECT OFF) set(PYBIND11_MASTER_PROJECT OFF)
set(pybind11_system SYSTEM) set(pybind11_system SYSTEM)
set(_pybind11_findpython_default OFF)
endif() endif()
# Options # Options
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_NOPYTHON "Disable search for Python" OFF) option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
option(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION
"To enforce that a handle_type_name<> specialization exists" OFF)
option(PYBIND11_SIMPLE_GIL_MANAGEMENT
"Use simpler GIL management logic that does not support disassociation" OFF)
option(PYBIND11_NUMPY_1_ONLY
"Disable NumPy 2 support to avoid changes to previous pybind11 versions." OFF)
set(PYBIND11_INTERNALS_VERSION
""
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
if(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
add_compile_definitions(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
endif()
if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT)
endif()
if(PYBIND11_NUMPY_1_ONLY)
add_compile_definitions(PYBIND11_NUMPY_1_ONLY)
endif()
cmake_dependent_option( cmake_dependent_option(
USE_PYTHON_INCLUDE_DIR USE_PYTHON_INCLUDE_DIR
"Install pybind11 headers in Python include directory instead of default installation prefix" "Install pybind11 headers in Python include directory instead of default installation prefix"
OFF "PYBIND11_INSTALL" OFF) OFF "PYBIND11_INSTALL" OFF)
cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default} cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" OFF
"NOT CMAKE_VERSION VERSION_LESS 3.12" OFF) "NOT CMAKE_VERSION VERSION_LESS 3.12" OFF)
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
# (makes transition easier while we support both modes).
if(PYBIND11_MASTER_PROJECT
AND PYBIND11_FINDPYTHON
AND DEFINED PYTHON_EXECUTABLE
AND NOT DEFINED Python_EXECUTABLE)
set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
endif()
# NB: when adding a header don't forget to also add it to setup.py # NB: when adding a header don't forget to also add it to setup.py
set(PYBIND11_HEADERS set(PYBIND11_HEADERS
include/pybind11/detail/class.h include/pybind11/detail/class.h
include/pybind11/detail/common.h include/pybind11/detail/common.h
include/pybind11/detail/cpp_conduit.h
include/pybind11/detail/descr.h include/pybind11/detail/descr.h
include/pybind11/detail/init.h include/pybind11/detail/init.h
include/pybind11/detail/internals.h include/pybind11/detail/internals.h
include/pybind11/detail/type_caster_base.h
include/pybind11/detail/typeid.h include/pybind11/detail/typeid.h
include/pybind11/attr.h include/pybind11/attr.h
include/pybind11/buffer_info.h include/pybind11/buffer_info.h
@ -162,13 +107,8 @@ set(PYBIND11_HEADERS
include/pybind11/complex.h include/pybind11/complex.h
include/pybind11/options.h include/pybind11/options.h
include/pybind11/eigen.h include/pybind11/eigen.h
include/pybind11/eigen/common.h
include/pybind11/eigen/matrix.h
include/pybind11/eigen/tensor.h
include/pybind11/embed.h include/pybind11/embed.h
include/pybind11/eval.h include/pybind11/eval.h
include/pybind11/gil.h
include/pybind11/gil_safe_call_once.h
include/pybind11/iostream.h include/pybind11/iostream.h
include/pybind11/functional.h include/pybind11/functional.h
include/pybind11/numpy.h include/pybind11/numpy.h
@ -176,10 +116,7 @@ set(PYBIND11_HEADERS
include/pybind11/pybind11.h include/pybind11/pybind11.h
include/pybind11/pytypes.h include/pybind11/pytypes.h
include/pybind11/stl.h include/pybind11/stl.h
include/pybind11/stl_bind.h include/pybind11/stl_bind.h)
include/pybind11/stl/filesystem.h
include/pybind11/type_caster_pyobject_ptr.h
include/pybind11/typing.h)
# Compare with grep and warn if mismatched # Compare with grep and warn if mismatched
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
@ -222,33 +159,14 @@ endif()
# You can also place ifs *in* the Config.in, but not here. # You can also place ifs *in* the Config.in, but not here.
# This section builds targets, but does *not* touch Python # This section builds targets, but does *not* touch Python
# Non-IMPORT targets cannot be defined twice
if(NOT TARGET pybind11_headers)
# Build the headers-only target (no Python included):
# (long name used here to keep this from clashing in subdirectory mode)
add_library(pybind11_headers INTERFACE)
add_library(pybind11::pybind11_headers ALIAS pybind11_headers) # to match exported target
add_library(pybind11::headers ALIAS pybind11_headers) # easier to use/remember
target_include_directories( # Build the headers-only target (no Python included):
pybind11_headers ${pybind11_system} INTERFACE $<BUILD_INTERFACE:${pybind11_INCLUDE_DIR}> # (long name used here to keep this from clashing in subdirectory mode)
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) add_library(pybind11_headers INTERFACE)
add_library(pybind11::pybind11_headers ALIAS pybind11_headers) # to match exported target
target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals add_library(pybind11::headers ALIAS pybind11_headers) # easier to use/remember
cxx_right_angle_brackets)
if(NOT "${PYBIND11_INTERNALS_VERSION}" STREQUAL "")
target_compile_definitions(
pybind11_headers INTERFACE "PYBIND11_INTERNALS_VERSION=${PYBIND11_INTERNALS_VERSION}")
endif()
else()
# It is invalid to install a target twice, too.
set(PYBIND11_INSTALL OFF)
endif()
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake")
# https://github.com/jtojnar/cmake-snips/#concatenating-paths-when-building-pkg-config-files
# TODO: cmake 3.20 adds the cmake_path() function, which obsoletes this snippet
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/JoinPaths.cmake")
# Relative directory setting # Relative directory setting
if(USE_PYTHON_INCLUDE_DIR AND DEFINED Python_INCLUDE_DIRS) if(USE_PYTHON_INCLUDE_DIR AND DEFINED Python_INCLUDE_DIRS)
@ -257,18 +175,20 @@ elseif(USE_PYTHON_INCLUDE_DIR AND DEFINED PYTHON_INCLUDE_DIR)
file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS}) file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS})
endif() endif()
# Fill in headers target
target_include_directories(
pybind11_headers ${pybind11_system} INTERFACE $<BUILD_INTERFACE:${pybind11_INCLUDE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals
cxx_right_angle_brackets)
if(PYBIND11_INSTALL) if(PYBIND11_INSTALL)
install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
set(PYBIND11_CMAKECONFIG_INSTALL_DIR set(PYBIND11_CMAKECONFIG_INSTALL_DIR
"${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}" "${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}"
CACHE STRING "install path for pybind11Config.cmake") CACHE STRING "install path for pybind11Config.cmake")
if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
set(pybind11_INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
else()
set(pybind11_INCLUDEDIR "\$\{PACKAGE_PREFIX_DIR\}/${CMAKE_INSTALL_INCLUDEDIR}")
endif()
configure_package_config_file( configure_package_config_file(
tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
@ -313,30 +233,6 @@ if(PYBIND11_INSTALL)
NAMESPACE "pybind11::" NAMESPACE "pybind11::"
DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
# pkg-config support
if(NOT prefix_for_pc_file)
if(IS_ABSOLUTE "${CMAKE_INSTALL_DATAROOTDIR}")
set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}")
else()
set(pc_datarootdir "${CMAKE_INSTALL_DATAROOTDIR}")
if(CMAKE_VERSION VERSION_LESS 3.20)
set(prefix_for_pc_file "\${pcfiledir}/..")
while(pc_datarootdir)
get_filename_component(pc_datarootdir "${pc_datarootdir}" DIRECTORY)
string(APPEND prefix_for_pc_file "/..")
endwhile()
else()
cmake_path(RELATIVE_PATH CMAKE_INSTALL_PREFIX BASE_DIRECTORY CMAKE_INSTALL_DATAROOTDIR
OUTPUT_VARIABLE prefix_for_pc_file)
endif()
endif()
endif()
join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11.pc.in"
"${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc"
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")
# Uninstall target # Uninstall target
if(PYBIND11_MASTER_PROJECT) if(PYBIND11_MASTER_PROJECT)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in"

View File

@ -1,6 +1,6 @@
prune tests
recursive-include pybind11/include/pybind11 *.h recursive-include pybind11/include/pybind11 *.h
recursive-include pybind11 *.py recursive-include pybind11 *.py
recursive-include pybind11 py.typed recursive-include pybind11 py.typed
recursive-include pybind11 *.pyi
include pybind11/share/cmake/pybind11/*.cmake include pybind11/share/cmake/pybind11/*.cmake
include LICENSE README.rst SECURITY.md pyproject.toml setup.py setup.cfg include LICENSE README.rst pyproject.toml setup.py setup.cfg

View File

@ -3,16 +3,20 @@
**pybind11 — Seamless operability between C++11 and Python** **pybind11 — Seamless operability between C++11 and Python**
|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |GitHub Discussions| |CI| |Build status| |Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |CI| |Build status|
|Repology| |PyPI package| |Conda-forge| |Python Versions| .. warning::
`Setuptools example <https://github.com/pybind/python_example>`_ Combining older versions of pybind11 (< 2.6.0) with the brand-new Python
`Scikit-build example <https://github.com/pybind/scikit_build_example>`_ 3.9.0 will trigger undefined behavior that typically manifests as crashes
`CMake example <https://github.com/pybind/cmake_example>`_ during interpreter shutdown (but could also destroy your data. **You have been
warned.**)
.. start
We recommend that you wait for Python 3.9.1 slated for release in December,
which will include a `fix <https://github.com/python/cpython/pull/22670>`_
that resolves this problem. In the meantime, please update to the latest
version of pybind11 (2.6.0 or newer), which includes a temporary workaround
specifically when Python 3.9.0 is detected at runtime.
**pybind11** is a lightweight header-only library that exposes C++ types **pybind11** is a lightweight header-only library that exposes C++ types
in Python and vice versa, mainly to create Python bindings of existing in Python and vice versa, mainly to create Python bindings of existing
@ -32,14 +36,14 @@ this heavy machinery has become an excessively large and unnecessary
dependency. dependency.
Think of this library as a tiny self-contained version of Boost.Python Think of this library as a tiny self-contained version of Boost.Python
with everything stripped away that isn't relevant for binding with everything stripped away that isnt relevant for binding
generation. Without comments, the core header files only require ~4K generation. Without comments, the core header files only require ~4K
lines of code and depend on Python (3.6+, or PyPy) and the C++ lines of code and depend on Python (2.7 or 3.5+, or PyPy) and the C++
standard library. This compact implementation was possible thanks to standard library. This compact implementation was possible thanks to
some C++11 language features (specifically: tuples, lambda functions and some of the new C++11 language features (specifically: tuples, lambda
variadic templates). Since its creation, this library has grown beyond functions and variadic templates). Since its creation, this library has
Boost.Python in many ways, leading to dramatically simpler binding code in many grown beyond Boost.Python in many ways, leading to dramatically simpler
common situations. binding code in many common situations.
Tutorial and reference documentation is provided at Tutorial and reference documentation is provided at
`pybind11.readthedocs.io <https://pybind11.readthedocs.io/en/latest>`_. `pybind11.readthedocs.io <https://pybind11.readthedocs.io/en/latest>`_.
@ -71,7 +75,6 @@ pybind11 can map the following core C++ features to Python:
- Internal references with correct reference counting - Internal references with correct reference counting
- C++ classes with virtual (and pure virtual) methods can be extended - C++ classes with virtual (and pure virtual) methods can be extended
in Python in Python
- Integrated NumPy support (NumPy 2 requires pybind11 2.12+)
Goodies Goodies
------- -------
@ -79,8 +82,8 @@ Goodies
In addition to the core functionality, pybind11 provides some extra In addition to the core functionality, pybind11 provides some extra
goodies: goodies:
- Python 3.6+, and PyPy3 7.3 are supported with an implementation-agnostic - Python 2.7, 3.5+, and PyPy/PyPy3 7.3 are supported with an
interface (pybind11 2.9 was the last version to support Python 2 and 3.5). implementation-agnostic interface.
- It is possible to bind C++11 lambda functions with captured - It is possible to bind C++11 lambda functions with captured
variables. The lambda capture data is stored inside the resulting variables. The lambda capture data is stored inside the resulting
@ -89,8 +92,8 @@ goodies:
- pybind11 uses C++11 move constructors and move assignment operators - pybind11 uses C++11 move constructors and move assignment operators
whenever possible to efficiently transfer custom data types. whenever possible to efficiently transfer custom data types.
- It's easy to expose the internal storage of custom data types through - Its easy to expose the internal storage of custom data types through
Pythons' buffer protocols. This is handy e.g. for fast conversion Pythons buffer protocols. This is handy e.g. for fast conversion
between C++ matrix classes like Eigen and NumPy without expensive between C++ matrix classes like Eigen and NumPy without expensive
copy operations. copy operations.
@ -98,7 +101,7 @@ goodies:
transparently applied to all entries of one or more NumPy array transparently applied to all entries of one or more NumPy array
arguments. arguments.
- Python's slice-based access and assignment operations can be - Pythons slice-based access and assignment operations can be
supported with just a few lines of code. supported with just a few lines of code.
- Everything is contained in just a few header files; there is no need - Everything is contained in just a few header files; there is no need
@ -107,7 +110,7 @@ goodies:
- Binaries are generally smaller by a factor of at least 2 compared to - Binaries are generally smaller by a factor of at least 2 compared to
equivalent bindings generated by Boost.Python. A recent pybind11 equivalent bindings generated by Boost.Python. A recent pybind11
conversion of PyRosetta, an enormous Boost.Python binding project, conversion of PyRosetta, an enormous Boost.Python binding project,
`reported <https://graylab.jhu.edu/Sergey/2016.RosettaCon/PyRosetta-4.pdf>`_ `reported <http://graylab.jhu.edu/RosettaCon2016/PyRosetta-4.pdf>`_
a binary size reduction of **5.4x** and compile time reduction by a binary size reduction of **5.4x** and compile time reduction by
**5.8x**. **5.8x**.
@ -120,14 +123,15 @@ goodies:
Supported compilers Supported compilers
------------------- -------------------
1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or 1. Clang/LLVM 3.3 or newer (for Apple Xcodes clang, this is 5.0.0 or
newer) newer)
2. GCC 4.8 or newer 2. GCC 4.8 or newer
3. Microsoft Visual Studio 2017 or newer 3. Microsoft Visual Studio 2015 Update 3 or newer
4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI) 4. Intel C++ compiler 18 or newer
5. Cygwin/GCC (previously tested on 2.5.1) (`possible issue <https://github.com/pybind/pybind11/pull/2573>`_ on 20.2)
6. NVCC (CUDA 11.0 tested in CI) 5. Cygwin/GCC (tested on 2.5.1)
7. NVIDIA PGI (20.9 tested in CI) 6. NVCC (CUDA 11.0 tested)
7. NVIDIA PGI (20.7 and 20.9 tested)
About About
----- -----
@ -135,9 +139,9 @@ About
This project was created by `Wenzel This project was created by `Wenzel
Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or
improvements to the code were contributed by Jonas Adler, Lori A. Burns, improvements to the code were contributed by Jonas Adler, Lori A. Burns,
Sylvain Corlay, Eric Cousineau, Aaron Gokaslan, Ralf Grosse-Kunstleve, Trent Houliston, Axel Sylvain Corlay, Eric Cousineau, Ralf Grosse-Kunstleve, Trent Houliston, Axel
Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov, Johan Mabille, Tomasz Miąsko, Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov Johan Mabille, Tomasz Miąsko,
Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim
Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart. Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart.
We thank Google for a generous financial contribution to the continuous We thank Google for a generous financial contribution to the continuous
@ -161,7 +165,7 @@ to the terms and conditions of this license.
.. |Latest Documentation Status| image:: https://readthedocs.org/projects/pybind11/badge?version=latest .. |Latest Documentation Status| image:: https://readthedocs.org/projects/pybind11/badge?version=latest
:target: http://pybind11.readthedocs.org/en/latest :target: http://pybind11.readthedocs.org/en/latest
.. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue.svg .. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue
:target: http://pybind11.readthedocs.org/en/stable :target: http://pybind11.readthedocs.org/en/stable
.. |Gitter chat| image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg .. |Gitter chat| image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg
:target: https://gitter.im/pybind/Lobby :target: https://gitter.im/pybind/Lobby
@ -169,13 +173,3 @@ to the terms and conditions of this license.
:target: https://github.com/pybind/pybind11/actions :target: https://github.com/pybind/pybind11/actions
.. |Build status| image:: https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true .. |Build status| image:: https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true
:target: https://ci.appveyor.com/project/wjakob/pybind11 :target: https://ci.appveyor.com/project/wjakob/pybind11
.. |PyPI package| image:: https://img.shields.io/pypi/v/pybind11.svg
:target: https://pypi.org/project/pybind11/
.. |Conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pybind11.svg
:target: https://github.com/conda-forge/pybind11-feedstock
.. |Repology| image:: https://repology.org/badge/latest-versions/python:pybind11.svg
:target: https://repology.org/project/python:pybind11/versions
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg
:target: https://pypi.org/project/pybind11/
.. |GitHub Discussions| image:: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github
:target: https://github.com/pybind/pybind11/discussions

View File

@ -1,13 +0,0 @@
# Security Policy
## Supported Versions
Security updates are applied only to the latest release.
## Reporting a Vulnerability
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
Please disclose it at [security advisory](https://github.com/pybind/pybind11/security/advisories/new).
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.

View File

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

View File

@ -1,3 +0,0 @@
.highlight .go {
color: #707070;
}

View File

@ -0,0 +1,11 @@
.wy-table-responsive table td,
.wy-table-responsive table th {
white-space: initial !important;
}
.rst-content table.docutils td {
vertical-align: top !important;
}
div[class^='highlight'] pre {
white-space: pre;
white-space: pre-wrap;
}

View File

@ -26,9 +26,7 @@ The following Python snippet demonstrates the intended usage from the Python sid
def __int__(self): def __int__(self):
return 123 return 123
from example import print from example import print
print(A()) print(A())
To register the necessary conversion routines, it is necessary to add an To register the necessary conversion routines, it is necessary to add an
@ -38,7 +36,7 @@ type is explicitly allowed.
.. code-block:: cpp .. code-block:: cpp
namespace PYBIND11_NAMESPACE { namespace detail { namespace pybind11 { namespace detail {
template <> struct type_caster<inty> { template <> struct type_caster<inty> {
public: public:
/** /**
@ -46,7 +44,7 @@ type is explicitly allowed.
* function signatures and declares a local variable * function signatures and declares a local variable
* 'value' of type inty * 'value' of type inty
*/ */
PYBIND11_TYPE_CASTER(inty, const_name("inty")); PYBIND11_TYPE_CASTER(inty, _("inty"));
/** /**
* Conversion part 1 (Python->C++): convert a PyObject into a inty * Conversion part 1 (Python->C++): convert a PyObject into a inty
@ -78,7 +76,7 @@ type is explicitly allowed.
return PyLong_FromLong(src.long_value); return PyLong_FromLong(src.long_value);
} }
}; };
}} // namespace PYBIND11_NAMESPACE::detail }} // namespace pybind11::detail
.. note:: .. note::

View File

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

View File

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

View File

@ -42,7 +42,7 @@ types:
.. code-block:: cpp .. code-block:: cpp
// `boost::optional` as an example -- can be any `std::optional`-like container // `boost::optional` as an example -- can be any `std::optional`-like container
namespace PYBIND11_NAMESPACE { namespace detail { namespace pybind11 { namespace detail {
template <typename T> template <typename T>
struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {}; struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {};
}} }}
@ -54,7 +54,7 @@ for custom variant types:
.. code-block:: cpp .. code-block:: cpp
// `boost::variant` as an example -- can be any `std::variant`-like container // `boost::variant` as an example -- can be any `std::variant`-like container
namespace PYBIND11_NAMESPACE { namespace detail { namespace pybind11 { namespace detail {
template <typename... Ts> template <typename... Ts>
struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {}; struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};
@ -66,27 +66,18 @@ for custom variant types:
return boost::apply_visitor(args...); return boost::apply_visitor(args...);
} }
}; };
}} // namespace PYBIND11_NAMESPACE::detail }} // namespace pybind11::detail
The ``visit_helper`` specialization is not required if your ``name::variant`` provides The ``visit_helper`` specialization is not required if your ``name::variant`` provides
a ``name::visit()`` function. For any other function name, the specialization must be a ``name::visit()`` function. For any other function name, the specialization must be
included to tell pybind11 how to visit the variant. included to tell pybind11 how to visit the variant.
.. warning::
When converting a ``variant`` type, pybind11 follows the same rules as when
determining which function overload to call (:ref:`overload_resolution`), and
so the same caveats hold. In particular, the order in which the ``variant``'s
alternatives are listed is important, since pybind11 will try conversions in
this order. This means that, for example, when converting ``variant<int, bool>``,
the ``bool`` variant will never be selected, as any Python ``bool`` is already
an ``int`` and is convertible to a C++ ``int``. Changing the order of alternatives
(and using ``variant<bool, int>``, in this example) provides a solution.
.. note:: .. note::
pybind11 only supports the modern implementation of ``boost::variant`` pybind11 only supports the modern implementation of ``boost::variant``
which makes use of variadic templates. This requires Boost 1.56 or newer. which makes use of variadic templates. This requires Boost 1.56 or newer.
Additionally, on Windows, MSVC 2017 is required because ``boost::variant``
falls back to the old non-variadic implementation on MSVC 2015.
.. _opaque: .. _opaque:

View File

@ -1,6 +1,14 @@
Strings, bytes and Unicode conversions Strings, bytes and Unicode conversions
###################################### ######################################
.. note::
This section discusses string handling in terms of Python 3 strings. For
Python 2.7, replace all occurrences of ``str`` with ``unicode`` and
``bytes`` with ``str``. Python 2.7 users may find it best to use ``from
__future__ import unicode_literals`` to avoid unintentionally using ``str``
instead of ``unicode``.
Passing Python strings to C++ Passing Python strings to C++
============================= =============================
@ -28,13 +36,13 @@ everywhere <http://utf8everywhere.org/>`_.
} }
); );
.. code-block:: pycon .. code-block:: python
>>> utf8_test("🎂") >>> utf8_test('🎂')
utf-8 is icing on the cake. utf-8 is icing on the cake.
🎂 🎂
>>> utf8_charptr("🍕") >>> utf8_charptr('🍕')
My favorite food is My favorite food is
🍕 🍕
@ -50,9 +58,9 @@ Passing bytes to C++
-------------------- --------------------
A Python ``bytes`` object will be passed to C++ functions that accept A Python ``bytes`` object will be passed to C++ functions that accept
``std::string`` or ``char*`` *without* conversion. In order to make a function ``std::string`` or ``char*`` *without* conversion. On Python 3, in order to
*only* accept ``bytes`` (and not ``str``), declare it as taking a ``py::bytes`` make a function *only* accept ``bytes`` (and not ``str``), declare it as taking
argument. a ``py::bytes`` argument.
Returning C++ strings to Python Returning C++ strings to Python
@ -72,7 +80,7 @@ raise a ``UnicodeDecodeError``.
} }
); );
.. code-block:: pycon .. code-block:: python
>>> isinstance(example.std_string_return(), str) >>> isinstance(example.std_string_return(), str)
True True
@ -101,23 +109,19 @@ conversion has the same overhead as implicit conversion.
m.def("str_output", m.def("str_output",
[]() { []() {
std::string s = "Send your r\xe9sum\xe9 to Alice in HR"; // Latin-1 std::string s = "Send your r\xe9sum\xe9 to Alice in HR"; // Latin-1
py::handle py_s = PyUnicode_DecodeLatin1(s.data(), s.length(), nullptr); py::str py_s = PyUnicode_DecodeLatin1(s.data(), s.length());
if (!py_s) { return py_s;
throw py::error_already_set();
}
return py::reinterpret_steal<py::str>(py_s);
} }
); );
.. code-block:: pycon .. code-block:: python
>>> str_output() >>> str_output()
'Send your résumé to Alice in HR' 'Send your résumé to Alice in HR'
The `Python C API The `Python C API
<https://docs.python.org/3/c-api/unicode.html#built-in-codecs>`_ provides <https://docs.python.org/3/c-api/unicode.html#built-in-codecs>`_ provides
several built-in codecs. Note that these all return *new* references, so several built-in codecs.
use :cpp:func:`reinterpret_steal` when converting them to a :cpp:class:`str`.
One could also use a third party encoding library such as libiconv to transcode One could also use a third party encoding library such as libiconv to transcode
@ -139,7 +143,7 @@ returned to Python as ``bytes``, then one can return the data as a
} }
); );
.. code-block:: pycon .. code-block:: python
>>> example.return_bytes() >>> example.return_bytes()
b'\xba\xd0\xba\xd0' b'\xba\xd0\xba\xd0'
@ -156,7 +160,7 @@ encoding, but cannot convert ``std::string`` back to ``bytes`` implicitly.
} }
); );
.. code-block:: pycon .. code-block:: python
>>> isinstance(example.asymmetry(b"have some bytes"), str) >>> isinstance(example.asymmetry(b"have some bytes"), str)
True True
@ -200,6 +204,11 @@ decoded to Python ``str``.
} }
); );
.. warning::
Wide character strings may not work as described on Python 2.7 or Python
3.3 compiled with ``--enable-unicode=ucs2``.
Strings in multibyte encodings such as Shift-JIS must transcoded to a Strings in multibyte encodings such as Shift-JIS must transcoded to a
UTF-8/16/32 before being returned to Python. UTF-8/16/32 before being returned to Python.
@ -220,16 +229,16 @@ character.
m.def("pass_char", [](char c) { return c; }); m.def("pass_char", [](char c) { return c; });
m.def("pass_wchar", [](wchar_t w) { return w; }); m.def("pass_wchar", [](wchar_t w) { return w; });
.. code-block:: pycon .. code-block:: python
>>> example.pass_char("A") >>> example.pass_char('A')
'A' 'A'
While C++ will cast integers to character types (``char c = 0x65;``), pybind11 While C++ will cast integers to character types (``char c = 0x65;``), pybind11
does not convert Python integers to characters implicitly. The Python function does not convert Python integers to characters implicitly. The Python function
``chr()`` can be used to convert integers to characters. ``chr()`` can be used to convert integers to characters.
.. code-block:: pycon .. code-block:: python
>>> example.pass_char(0x65) >>> example.pass_char(0x65)
TypeError TypeError
@ -250,17 +259,17 @@ a combining acute accent). The combining character will be lost if the
two-character sequence is passed as an argument, even though it renders as a two-character sequence is passed as an argument, even though it renders as a
single grapheme. single grapheme.
.. code-block:: pycon .. code-block:: python
>>> example.pass_wchar("é") >>> example.pass_wchar('é')
'é' 'é'
>>> combining_e_acute = "e" + "\u0301" >>> combining_e_acute = 'e' + '\u0301'
>>> combining_e_acute >>> combining_e_acute
'é' 'é'
>>> combining_e_acute == "é" >>> combining_e_acute == 'é'
False False
>>> example.pass_wchar(combining_e_acute) >>> example.pass_wchar(combining_e_acute)
@ -269,9 +278,9 @@ single grapheme.
Normalizing combining characters before passing the character literal to C++ Normalizing combining characters before passing the character literal to C++
may resolve *some* of these issues: may resolve *some* of these issues:
.. code-block:: pycon .. code-block:: python
>>> example.pass_wchar(unicodedata.normalize("NFC", combining_e_acute)) >>> example.pass_wchar(unicodedata.normalize('NFC', combining_e_acute))
'é' 'é'
In some languages (Thai for example), there are `graphemes that cannot be In some languages (Thai for example), there are `graphemes that cannot be

View File

@ -9,7 +9,7 @@ that you are already familiar with the basics from :doc:`/classes`.
Overriding virtual functions in Python Overriding virtual functions in Python
====================================== ======================================
Suppose that a C++ class or interface has a virtual function that we'd like Suppose that a C++ class or interface has a virtual function that we'd like to
to override from within Python (we'll focus on the class ``Animal``; ``Dog`` is to override from within Python (we'll focus on the class ``Animal``; ``Dog`` is
given as a specific example of how one would do this with traditional C++ given as a specific example of how one would do this with traditional C++
code). code).
@ -133,14 +133,14 @@ a virtual method call.
>>> from example import * >>> from example import *
>>> d = Dog() >>> d = Dog()
>>> call_go(d) >>> call_go(d)
'woof! woof! woof! ' u'woof! woof! woof! '
>>> class Cat(Animal): >>> class Cat(Animal):
... def go(self, n_times): ... def go(self, n_times):
... return "meow! " * n_times ... return "meow! " * n_times
... ...
>>> c = Cat() >>> c = Cat()
>>> call_go(c) >>> call_go(c)
'meow! meow! meow! ' u'meow! meow! meow! '
If you are defining a custom constructor in a derived Python class, you *must* If you are defining a custom constructor in a derived Python class, you *must*
ensure that you explicitly call the bound C++ constructor using ``__init__``, ensure that you explicitly call the bound C++ constructor using ``__init__``,
@ -159,9 +159,8 @@ Here is an example:
class Dachshund(Dog): class Dachshund(Dog):
def __init__(self, name): def __init__(self, name):
Dog.__init__(self) # Without this, a TypeError is raised. Dog.__init__(self) # Without this, a TypeError is raised.
self.name = name self.name = name
def bark(self): def bark(self):
return "yap!" return "yap!"
@ -260,7 +259,7 @@ override the ``name()`` method):
.. note:: .. note::
Note the trailing commas in the ``PYBIND11_OVERRIDE`` calls to ``name()`` Note the trailing commas in the ``PYBIND11_OVERIDE`` calls to ``name()``
and ``bark()``. These are needed to portably implement a trampoline for a and ``bark()``. These are needed to portably implement a trampoline for a
function that does not take any arguments. For functions that take function that does not take any arguments. For functions that take
a nonzero number of arguments, the trailing comma must be omitted. a nonzero number of arguments, the trailing comma must be omitted.
@ -805,7 +804,7 @@ to bind these two functions:
} }
)); ));
The ``__setstate__`` part of the ``py::pickle()`` definition follows the same The ``__setstate__`` part of the ``py::picke()`` definition follows the same
rules as the single-argument version of ``py::init()``. The return type can be rules as the single-argument version of ``py::init()``. The return type can be
a value, pointer or holder type. See :ref:`custom_constructors` for details. a value, pointer or holder type. See :ref:`custom_constructors` for details.
@ -813,21 +812,26 @@ An instance can now be pickled as follows:
.. code-block:: python .. code-block:: python
import pickle try:
import cPickle as pickle # Use cPickle on Python 2.7
except ImportError:
import pickle
p = Pickleable("test_value") p = Pickleable("test_value")
p.setExtra(15) p.setExtra(15)
data = pickle.dumps(p) data = pickle.dumps(p, 2)
.. note:: .. note::
If given, the second argument to ``dumps`` must be 2 or larger - 0 and 1 are Note that only the cPickle module is supported on Python 2.7.
not supported. Newer versions are also fine; for instance, specify ``-1`` to
always use the latest available version. Beware: failure to follow these The second argument to ``dumps`` is also crucial: it selects the pickle
instructions will cause important pybind11 memory allocation routines to be protocol version 2, since the older version 1 is not supported. Newer
skipped during unpickling, which will likely lead to memory corruption versions are also fine—for instance, specify ``-1`` to always use the
and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and latest available version. Beware: failure to follow these instructions
version 4 for Python 3.8+. will cause important pybind11 memory allocation routines to be skipped
during unpickling, which will likely lead to memory corruption and/or
segmentation faults.
.. seealso:: .. seealso::
@ -844,9 +848,11 @@ Python normally uses references in assignments. Sometimes a real copy is needed
to prevent changing all copies. The ``copy`` module [#f5]_ provides these to prevent changing all copies. The ``copy`` module [#f5]_ provides these
capabilities. capabilities.
A class with pickle support is automatically also (deep)copy On Python 3, a class with pickle support is automatically also (deep)copy
compatible. However, performance can be improved by adding custom compatible. However, performance can be improved by adding custom
``__copy__`` and ``__deepcopy__`` methods. ``__copy__`` and ``__deepcopy__`` methods. With Python 2.7, these custom methods
are mandatory for (deep)copy compatibility, because pybind11 only supports
cPickle.
For simple classes (deep)copy can be enabled by using the copy constructor, For simple classes (deep)copy can be enabled by using the copy constructor,
which should look as follows: which should look as follows:
@ -1118,6 +1124,13 @@ described trampoline:
py::class_<A, Trampoline>(m, "A") // <-- `Trampoline` here py::class_<A, Trampoline>(m, "A") // <-- `Trampoline` here
.def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`! .def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`!
.. note::
MSVC 2015 has a compiler bug (fixed in version 2017) which
requires a more explicit function binding in the form of
``.def("foo", static_cast<int (A::*)() const>(&Publicist::foo));``
where ``int (A::*)() const`` is the type of ``A::foo``.
Binding final classes Binding final classes
===================== =====================
@ -1140,65 +1153,12 @@ error:
>>> class PyFinalChild(IsFinal): >>> class PyFinalChild(IsFinal):
... pass ... pass
...
TypeError: type 'IsFinal' is not an acceptable base type TypeError: type 'IsFinal' is not an acceptable base type
.. note:: This attribute is currently ignored on PyPy .. note:: This attribute is currently ignored on PyPy
.. versionadded:: 2.6 .. versionadded:: 2.6
Binding classes with template parameters
========================================
pybind11 can also wrap classes that have template parameters. Consider these classes:
.. code-block:: cpp
struct Cat {};
struct Dog {};
template <typename PetType>
struct Cage {
Cage(PetType& pet);
PetType& get();
};
C++ templates may only be instantiated at compile time, so pybind11 can only
wrap instantiated templated classes. You cannot wrap a non-instantiated template:
.. code-block:: cpp
// BROKEN (this will not compile)
py::class_<Cage>(m, "Cage");
.def("get", &Cage::get);
You must explicitly specify each template/type combination that you want to
wrap separately.
.. code-block:: cpp
// ok
py::class_<Cage<Cat>>(m, "CatCage")
.def("get", &Cage<Cat>::get);
// ok
py::class_<Cage<Dog>>(m, "DogCage")
.def("get", &Cage<Dog>::get);
If your class methods have template parameters you can wrap those as well,
but once again each instantiation must be explicitly specified:
.. code-block:: cpp
typename <typename T>
struct MyClass {
template <typename V>
T fn(V v);
};
py::class<MyClass<int>>(m, "MyClassT")
.def("fn", &MyClass<int>::fn<std::string>);
Custom automatic downcasters Custom automatic downcasters
============================ ============================
@ -1228,7 +1188,7 @@ whether a downcast is safe, you can proceed by specializing the
std::string bark() const { return sound; } std::string bark() const { return sound; }
}; };
namespace PYBIND11_NAMESPACE { namespace pybind11 {
template<> struct polymorphic_type_hook<Pet> { template<> struct polymorphic_type_hook<Pet> {
static const void *get(const Pet *src, const std::type_info*& type) { static const void *get(const Pet *src, const std::type_info*& type) {
// note that src may be nullptr // note that src may be nullptr
@ -1239,7 +1199,7 @@ whether a downcast is safe, you can proceed by specializing the
return src; return src;
} }
}; };
} // namespace PYBIND11_NAMESPACE } // namespace pybind11
When pybind11 wants to convert a C++ pointer of type ``Base*`` to a When pybind11 wants to convert a C++ pointer of type ``Base*`` to a
Python object, it calls ``polymorphic_type_hook<Base>::get()`` to Python object, it calls ``polymorphic_type_hook<Base>::get()`` to
@ -1287,7 +1247,7 @@ Accessing the type object
You can get the type object from a C++ class that has already been registered using: You can get the type object from a C++ class that has already been registered using:
.. code-block:: cpp .. code-block:: python
py::type T_py = py::type::of<T>(); py::type T_py = py::type::of<T>();
@ -1299,37 +1259,3 @@ object, just like ``type(ob)`` in Python.
Other types, like ``py::type::of<int>()``, do not work, see :ref:`type-conversions`. Other types, like ``py::type::of<int>()``, do not work, see :ref:`type-conversions`.
.. versionadded:: 2.6 .. versionadded:: 2.6
Custom type setup
=================
For advanced use cases, such as enabling garbage collection support, you may
wish to directly manipulate the ``PyHeapTypeObject`` corresponding to a
``py::class_`` definition.
You can do that using ``py::custom_type_setup``:
.. code-block:: cpp
struct OwnsPythonObjects {
py::object value = py::none();
};
py::class_<OwnsPythonObjects> cls(
m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) {
auto *type = &heap_type->ht_type;
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) {
auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
Py_VISIT(self.value.ptr());
return 0;
};
type->tp_clear = [](PyObject *self_base) {
auto &self = py::cast<OwnsPythonObjects&>(py::handle(self_base));
self.value = py::none();
return 0;
};
}));
cls.def(py::init<>());
cls.def_readwrite("value", &OwnsPythonObjects::value);
.. versionadded:: 2.8

View File

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

View File

@ -43,28 +43,18 @@ at its exception handler.
| | of bounds access in ``__getitem__``, | | | of bounds access in ``__getitem__``, |
| | ``__setitem__``, etc.) | | | ``__setitem__``, etc.) |
+--------------------------------------+--------------------------------------+ +--------------------------------------+--------------------------------------+
| :class:`pybind11::value_error` | ``ValueError`` (used to indicate |
| | wrong value passed in |
| | ``container.remove(...)``) |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::key_error` | ``KeyError`` (used to indicate out | | :class:`pybind11::key_error` | ``KeyError`` (used to indicate out |
| | of bounds access in ``__getitem__``, | | | of bounds access in ``__getitem__``, |
| | ``__setitem__`` in dict-like | | | ``__setitem__`` in dict-like |
| | objects, etc.) | | | objects, etc.) |
+--------------------------------------+--------------------------------------+ +--------------------------------------+--------------------------------------+
| :class:`pybind11::value_error` | ``ValueError`` (used to indicate |
| | wrong value passed in |
| | ``container.remove(...)``) |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::type_error` | ``TypeError`` |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::buffer_error` | ``BufferError`` |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::import_error` | ``ImportError`` |
+--------------------------------------+--------------------------------------+
| :class:`pybind11::attribute_error` | ``AttributeError`` |
+--------------------------------------+--------------------------------------+
| Any other exception | ``RuntimeError`` |
+--------------------------------------+--------------------------------------+
Exception translation is not bidirectional. That is, *catching* the C++ Exception translation is not bidirectional. That is, *catching* the C++
exceptions defined above will not trap exceptions that originate from exceptions defined above above will not trap exceptions that originate from
Python. For that, catch :class:`pybind11::error_already_set`. See :ref:`below Python. For that, catch :class:`pybind11::error_already_set`. See :ref:`below
<handling_python_exceptions_cpp>` for further details. <handling_python_exceptions_cpp>` for further details.
@ -77,10 +67,9 @@ Registering custom translators
If the default exception conversion policy described above is insufficient, If the default exception conversion policy described above is insufficient,
pybind11 also provides support for registering custom exception translators. pybind11 also provides support for registering custom exception translators.
Similar to pybind11 classes, exception translators can be local to the module To register a simple exception conversion that translates a C++ exception into
they are defined in or global to the entire python session. To register a simple a new Python exception using the C++ exception's ``what()`` method, a helper
exception conversion that translates a C++ exception into a new Python exception function is available:
using the C++ exception's ``what()`` method, a helper function is available:
.. code-block:: cpp .. code-block:: cpp
@ -90,44 +79,35 @@ This call creates a Python exception class with the name ``PyExp`` in the given
module and automatically converts any encountered exceptions of type ``CppExp`` module and automatically converts any encountered exceptions of type ``CppExp``
into Python exceptions of type ``PyExp``. into Python exceptions of type ``PyExp``.
A matching function is available for registering a local exception translator:
.. code-block:: cpp
py::register_local_exception<CppExp>(module, "PyExp");
It is possible to specify base class for the exception using the third It is possible to specify base class for the exception using the third
parameter, a ``handle``: parameter, a `handle`:
.. code-block:: cpp .. code-block:: cpp
py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError); py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
py::register_local_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
Then ``PyExp`` can be caught both as ``PyExp`` and ``RuntimeError``. Then `PyExp` can be caught both as `PyExp` and `RuntimeError`.
The class objects of the built-in Python exceptions are listed in the Python The class objects of the built-in Python exceptions are listed in the Python
documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_. documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_.
The default base class is ``PyExc_Exception``. The default base class is `PyExc_Exception`.
When more advanced exception translation is needed, the functions When more advanced exception translation is needed, the function
``py::register_exception_translator(translator)`` and ``py::register_exception_translator(translator)`` can be used to register
``py::register_local_exception_translator(translator)`` can be used to register
functions that can translate arbitrary exception types (and which may include functions that can translate arbitrary exception types (and which may include
additional logic to do so). The functions takes a stateless callable (e.g. a additional logic to do so). The function takes a stateless callable (e.g. a
function pointer or a lambda function without captured variables) with the call function pointer or a lambda function without captured variables) with the call
signature ``void(std::exception_ptr)``. signature ``void(std::exception_ptr)``.
When a C++ exception is thrown, the registered exception translators are tried When a C++ exception is thrown, the registered exception translators are tried
in reverse order of registration (i.e. the last registered translator gets the in reverse order of registration (i.e. the last registered translator gets the
first shot at handling the exception). All local translators will be tried first shot at handling the exception).
before a global translator is tried.
Inside the translator, ``std::rethrow_exception`` should be used within Inside the translator, ``std::rethrow_exception`` should be used within
a try block to re-throw the exception. One or more catch clauses to catch a try block to re-throw the exception. One or more catch clauses to catch
the appropriate exceptions should then be used with each clause using the appropriate exceptions should then be used with each clause using
``py::set_error()`` (see below). ``PyErr_SetString`` to set a Python exception or ``ex(string)`` to set
the python exception to a custom exception type (see below).
To declare a custom Python exception type, declare a ``py::exception`` variable To declare a custom Python exception type, declare a ``py::exception`` variable
and use this in the associated exception translator (note: it is often useful and use this in the associated exception translator (note: it is often useful
@ -141,16 +121,14 @@ standard python RuntimeError:
.. code-block:: cpp .. code-block:: cpp
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> exc_storage; static py::exception<MyCustomException> exc(m, "MyCustomError");
exc_storage.call_once_and_store_result(
[&]() { return py::exception<MyCustomException>(m, "MyCustomError"); });
py::register_exception_translator([](std::exception_ptr p) { py::register_exception_translator([](std::exception_ptr p) {
try { try {
if (p) std::rethrow_exception(p); if (p) std::rethrow_exception(p);
} catch (const MyCustomException &e) { } catch (const MyCustomException &e) {
py::set_error(exc_storage.get_stored(), e.what()); exc(e.what());
} catch (const OtherException &e) { } catch (const OtherException &e) {
py::set_error(PyExc_RuntimeError, e.what()); PyErr_SetString(PyExc_RuntimeError, e.what());
} }
}); });
@ -169,7 +147,8 @@ section.
.. note:: .. note::
Call ``py::set_error()`` for every exception caught in a custom exception Call either ``PyErr_SetString`` or a custom exception's call
operator (``exc(string)``) for every exception caught in a custom exception
translator. Failure to do so will cause Python to crash with ``SystemError: translator. Failure to do so will cause Python to crash with ``SystemError:
error return without exception set``. error return without exception set``.
@ -177,60 +156,6 @@ section.
may be explicitly (re-)thrown to delegate it to the other, may be explicitly (re-)thrown to delegate it to the other,
previously-declared existing exception translators. previously-declared existing exception translators.
Note that ``libc++`` and ``libstdc++`` `behave differently under macOS
<https://stackoverflow.com/questions/19496643/using-clang-fvisibility-hidden-and-typeinfo-and-type-erasure/28827430>`_
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI
boundaries need to be explicitly exported, as exercised in
``tests/test_exceptions.h``. See also:
"Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
Local vs Global Exception Translators
=====================================
When a global exception translator is registered, it will be applied across all
modules in the reverse order of registration. This can create behavior where the
order of module import influences how exceptions are translated.
If module1 has the following translator:
.. code-block:: cpp
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (const std::invalid_argument &e) {
py::set_error(PyExc_ArgumentError, "module1 handled this");
}
}
and module2 has the following similar translator:
.. code-block:: cpp
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (const std::invalid_argument &e) {
py::set_error(PyExc_ArgumentError, "module2 handled this");
}
}
then which translator handles the invalid_argument will be determined by the
order that module1 and module2 are imported. Since exception translators are
applied in the reverse order of registration, which ever module was imported
last will "win" and that translator will be applied.
If there are multiple pybind11 modules that share exception types (either
standard built-in or custom) loaded into a single python instance and
consistent error handling behavior is needed, then local translators should be
used.
Changing the previous example to use ``register_local_exception_translator``
would mean that when invalid_argument is thrown in the module2 code, the
module2 translator will always handle it, while in module1, the module1
translator will do the same.
.. _handling_python_exceptions_cpp: .. _handling_python_exceptions_cpp:
Handling exceptions from Python in C++ Handling exceptions from Python in C++
@ -263,7 +188,7 @@ For example:
} catch (py::error_already_set &e) { } catch (py::error_already_set &e) {
if (e.matches(PyExc_FileNotFoundError)) { if (e.matches(PyExc_FileNotFoundError)) {
py::print("missing.txt not found"); py::print("missing.txt not found");
} else if (e.matches(PyExc_PermissionError)) { } else if (e.match(PyExc_PermissionError)) {
py::print("missing.txt found but not accessible"); py::print("missing.txt found but not accessible");
} else { } else {
throw; throw;
@ -312,11 +237,11 @@ error protocol, which is outlined here.
After calling the Python C API, if Python returns an error, After calling the Python C API, if Python returns an error,
``throw py::error_already_set();``, which allows pybind11 to deal with the ``throw py::error_already_set();``, which allows pybind11 to deal with the
exception and pass it back to the Python interpreter. This includes calls to exception and pass it back to the Python interpreter. This includes calls to
the error setting functions such as ``py::set_error()``. the error setting functions such as ``PyErr_SetString``.
.. code-block:: cpp .. code-block:: cpp
py::set_error(PyExc_TypeError, "C API type error demo"); PyErr_SetString(PyExc_TypeError, "C API type error demo");
throw py::error_already_set(); throw py::error_already_set();
// But it would be easier to simply... // But it would be easier to simply...
@ -328,34 +253,6 @@ Alternately, to ignore the error, call `PyErr_Clear
Any Python error must be thrown or cleared, or Python/pybind11 will be left in Any Python error must be thrown or cleared, or Python/pybind11 will be left in
an invalid state. an invalid state.
Chaining exceptions ('raise from')
==================================
Python has a mechanism for indicating that exceptions were caused by other
exceptions:
.. code-block:: py
try:
print(1 / 0)
except Exception as exc:
raise RuntimeError("could not divide by zero") from exc
To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It
sets the current python error indicator, so to continue propagating the exception
you should ``throw py::error_already_set()``.
.. code-block:: cpp
try {
py::eval("print(1 / 0"));
} catch (py::error_already_set &e) {
py::raise_from(e, PyExc_RuntimeError, "could not divide by zero");
throw py::error_already_set();
}
.. versionadded:: 2.8
.. _unraisable_exceptions: .. _unraisable_exceptions:
Handling unraisable exceptions Handling unraisable exceptions

View File

@ -16,7 +16,7 @@ lifetime of objects managed by them. This can lead to issues when creating
bindings for functions that return a non-trivial type. Just by looking at the bindings for functions that return a non-trivial type. Just by looking at the
type information, it is not clear whether Python should take charge of the type information, it is not clear whether Python should take charge of the
returned value and eventually free its resources, or if this is handled on the returned value and eventually free its resources, or if this is handled on the
C++ side. For this reason, pybind11 provides several *return value policy* C++ side. For this reason, pybind11 provides a several *return value policy*
annotations that can be passed to the :func:`module_::def` and annotations that can be passed to the :func:`module_::def` and
:func:`class_::def` functions. The default policy is :func:`class_::def` functions. The default policy is
:enum:`return_value_policy::automatic`. :enum:`return_value_policy::automatic`.
@ -50,7 +50,7 @@ implied transfer of ownership, i.e.:
.. code-block:: cpp .. code-block:: cpp
m.def("get_data", &get_data, py::return_value_policy::reference); m.def("get_data", &get_data, return_value_policy::reference);
On the other hand, this is not the right policy for many other situations, On the other hand, this is not the right policy for many other situations,
where ignoring ownership could lead to resource leaks. where ignoring ownership could lead to resource leaks.
@ -90,18 +90,17 @@ The following table provides an overview of available policies:
| | return value is referenced by Python. This is the default policy for | | | return value is referenced by Python. This is the default policy for |
| | property getters created via ``def_property``, ``def_readwrite``, etc. | | | property getters created via ``def_property``, ``def_readwrite``, etc. |
+--------------------------------------------------+----------------------------------------------------------------------------+ +--------------------------------------------------+----------------------------------------------------------------------------+
| :enum:`return_value_policy::automatic` | This policy falls back to the policy | | :enum:`return_value_policy::automatic` | **Default policy.** This policy falls back to the policy |
| | :enum:`return_value_policy::take_ownership` when the return value is a | | | :enum:`return_value_policy::take_ownership` when the return value is a |
| | pointer. Otherwise, it uses :enum:`return_value_policy::move` or | | | pointer. Otherwise, it uses :enum:`return_value_policy::move` or |
| | :enum:`return_value_policy::copy` for rvalue and lvalue references, | | | :enum:`return_value_policy::copy` for rvalue and lvalue references, |
| | respectively. See above for a description of what all of these different | | | respectively. See above for a description of what all of these different |
| | policies do. This is the default policy for ``py::class_``-wrapped types. | | | policies do. |
+--------------------------------------------------+----------------------------------------------------------------------------+ +--------------------------------------------------+----------------------------------------------------------------------------+
| :enum:`return_value_policy::automatic_reference` | As above, but use policy :enum:`return_value_policy::reference` when the | | :enum:`return_value_policy::automatic_reference` | As above, but use policy :enum:`return_value_policy::reference` when the |
| | return value is a pointer. This is the default conversion policy for | | | return value is a pointer. This is the default conversion policy for |
| | function arguments when calling Python functions manually from C++ code | | | function arguments when calling Python functions manually from C++ code |
| | (i.e. via ``handle::operator()``) and the casters in ``pybind11/stl.h``. | | | (i.e. via handle::operator()). You probably won't need to use this. |
| | You probably won't need to use this explicitly. |
+--------------------------------------------------+----------------------------------------------------------------------------+ +--------------------------------------------------+----------------------------------------------------------------------------+
Return value policies can also be applied to properties: Return value policies can also be applied to properties:
@ -120,7 +119,7 @@ targeted arguments can be passed through the :class:`cpp_function` constructor:
.. code-block:: cpp .. code-block:: cpp
class_<MyClass>(m, "MyClass") class_<MyClass>(m, "MyClass")
.def_property("data", .def_property("data"
py::cpp_function(&MyClass::getData, py::return_value_policy::copy), py::cpp_function(&MyClass::getData, py::return_value_policy::copy),
py::cpp_function(&MyClass::setData) py::cpp_function(&MyClass::setData)
); );
@ -183,9 +182,6 @@ relies on the ability to create a *weak reference* to the nurse object. When
the nurse object is not a pybind11-registered type and does not support weak the nurse object is not a pybind11-registered type and does not support weak
references, an exception will be thrown. references, an exception will be thrown.
If you use an incorrect argument index, you will get a ``RuntimeError`` saying
``Could not activate keep_alive!``. You should review the indices you're using.
Consider the following example: here, the binding code for a list append Consider the following example: here, the binding code for a list append
operation ties the lifetime of the newly added element to the underlying operation ties the lifetime of the newly added element to the underlying
container: container:
@ -232,7 +228,7 @@ is equivalent to the following pseudocode:
}); });
The only requirement is that ``T`` is default-constructible, but otherwise any The only requirement is that ``T`` is default-constructible, but otherwise any
scope guard will work. This is very useful in combination with ``gil_scoped_release``. scope guard will work. This is very useful in combination with `gil_scoped_release`.
See :ref:`gil`. See :ref:`gil`.
Multiple guards can also be specified as ``py::call_guard<T1, T2, T3...>``. The Multiple guards can also be specified as ``py::call_guard<T1, T2, T3...>``. The
@ -255,7 +251,7 @@ For instance, the following statement iterates over a Python ``dict``:
.. code-block:: cpp .. code-block:: cpp
void print_dict(const py::dict& dict) { void print_dict(py::dict dict) {
/* Easily interact with Python types */ /* Easily interact with Python types */
for (auto item : dict) for (auto item : dict)
std::cout << "key=" << std::string(py::str(item.first)) << ", " std::cout << "key=" << std::string(py::str(item.first)) << ", "
@ -272,7 +268,7 @@ And used in Python as usual:
.. code-block:: pycon .. code-block:: pycon
>>> print_dict({"foo": 123, "bar": "hello"}) >>> print_dict({'foo': 123, 'bar': 'hello'})
key=foo, value=123 key=foo, value=123
key=bar, value=hello key=bar, value=hello
@ -293,7 +289,7 @@ Such functions can also be created using pybind11:
.. code-block:: cpp .. code-block:: cpp
void generic(py::args args, const py::kwargs& kwargs) { void generic(py::args args, py::kwargs kwargs) {
/// .. do something with args /// .. do something with args
if (kwargs) if (kwargs)
/// .. do something with kwargs /// .. do something with kwargs
@ -306,9 +302,8 @@ The class ``py::args`` derives from ``py::tuple`` and ``py::kwargs`` derives
from ``py::dict``. from ``py::dict``.
You may also use just one or the other, and may combine these with other You may also use just one or the other, and may combine these with other
arguments. Note, however, that ``py::kwargs`` must always be the last argument arguments as long as the ``py::args`` and ``py::kwargs`` arguments are the last
of the function, and ``py::args`` implies that any further arguments are arguments accepted by the function.
keyword-only (see :ref:`keyword_only_arguments`).
Please refer to the other examples for details on how to iterate over these, Please refer to the other examples for details on how to iterate over these,
and on how to cast their entries into C++ objects. A demonstration is also and on how to cast their entries into C++ objects. A demonstration is also
@ -367,12 +362,10 @@ like so:
py::class_<MyClass>("MyClass") py::class_<MyClass>("MyClass")
.def("myFunction", py::arg("arg") = static_cast<SomeType *>(nullptr)); .def("myFunction", py::arg("arg") = static_cast<SomeType *>(nullptr));
.. _keyword_only_arguments:
Keyword-only arguments Keyword-only arguments
====================== ======================
Python implements keyword-only arguments by specifying an unnamed ``*`` Python 3 introduced keyword-only arguments by specifying an unnamed ``*``
argument in a function definition: argument in a function definition:
.. code-block:: python .. code-block:: python
@ -380,11 +373,10 @@ argument in a function definition:
def f(a, *, b): # a can be positional or via keyword; b must be via keyword def f(a, *, b): # a can be positional or via keyword; b must be via keyword
pass pass
f(a=1, b=2) # good f(a=1, b=2) # good
f(b=2, a=1) # good f(b=2, a=1) # good
f(1, b=2) # good f(1, b=2) # good
f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given
Pybind11 provides a ``py::kw_only`` object that allows you to implement Pybind11 provides a ``py::kw_only`` object that allows you to implement
the same behaviour by specifying the object between positional and keyword-only the same behaviour by specifying the object between positional and keyword-only
@ -395,19 +387,11 @@ argument annotations when registering the function:
m.def("f", [](int a, int b) { /* ... */ }, m.def("f", [](int a, int b) { /* ... */ },
py::arg("a"), py::kw_only(), py::arg("b")); py::arg("a"), py::kw_only(), py::arg("b"));
Note that you currently cannot combine this with a ``py::args`` argument. This
feature does *not* require Python 3 to work.
.. versionadded:: 2.6 .. versionadded:: 2.6
A ``py::args`` argument implies that any following arguments are keyword-only,
as if ``py::kw_only()`` had been specified in the same relative location of the
argument list as the ``py::args`` argument. The ``py::kw_only()`` may be
included to be explicit about this, but is not required.
.. versionchanged:: 2.9
This can now be combined with ``py::args``. Before, ``py::args`` could only
occur at the end of the argument list, or immediately before a ``py::kwargs``
argument at the end.
Positional-only arguments Positional-only arguments
========================= =========================
@ -540,8 +524,6 @@ The default behaviour when the tag is unspecified is to allow ``None``.
not allow ``None`` as argument. To pass optional argument of these copied types consider not allow ``None`` as argument. To pass optional argument of these copied types consider
using ``std::optional<T>`` using ``std::optional<T>``
.. _overload_resolution:
Overload resolution order Overload resolution order
========================= =========================
@ -577,38 +559,3 @@ prefers earlier-defined overloads to later-defined ones.
.. versionadded:: 2.6 .. versionadded:: 2.6
The ``py::prepend()`` tag. The ``py::prepend()`` tag.
Binding functions with template parameters
==========================================
You can bind functions that have template parameters. Here's a function:
.. code-block:: cpp
template <typename T>
void set(T t);
C++ templates cannot be instantiated at runtime, so you cannot bind the
non-instantiated function:
.. code-block:: cpp
// BROKEN (this will not compile)
m.def("set", &set);
You must bind each instantiated function template separately. You may bind
each instantiation with the same name, which will be treated the same as
an overloaded function:
.. code-block:: cpp
m.def("set", &set<int>);
m.def("set", &set<std::string>);
Sometimes it's more clear to bind them with separate names, which is also
an option:
.. code-block:: cpp
m.def("setInt", &set<int>);
m.def("setString", &set<std::string>);

View File

@ -39,42 +39,15 @@ The ``PYBIND11_MAKE_OPAQUE`` macro does *not* require the above workarounds.
Global Interpreter Lock (GIL) Global Interpreter Lock (GIL)
============================= =============================
The Python C API dictates that the Global Interpreter Lock (GIL) must always When calling a C++ function from Python, the GIL is always held.
be held by the current thread to safely access Python objects. As a result,
when Python calls into C++ via pybind11 the GIL must be held, and pybind11
will never implicitly release the GIL.
.. code-block:: cpp
void my_function() {
/* GIL is held when this function is called from Python */
}
PYBIND11_MODULE(example, m) {
m.def("my_function", &my_function);
}
pybind11 will ensure that the GIL is held when it knows that it is calling
Python code. For example, if a Python callback is passed to C++ code via
``std::function``, when C++ code calls the function the built-in wrapper
will acquire the GIL before calling the Python callback. Similarly, the
``PYBIND11_OVERRIDE`` family of macros will acquire the GIL before calling
back into Python.
When writing C++ code that is called from other C++ code, if that code accesses
Python state, it must explicitly acquire and release the GIL.
The classes :class:`gil_scoped_release` and :class:`gil_scoped_acquire` can be The classes :class:`gil_scoped_release` and :class:`gil_scoped_acquire` can be
used to acquire and release the global interpreter lock in the body of a C++ used to acquire and release the global interpreter lock in the body of a C++
function call. In this way, long-running C++ code can be parallelized using function call. In this way, long-running C++ code can be parallelized using
multiple Python threads, **but great care must be taken** when any multiple Python threads. Taking :ref:`overriding_virtuals` as an example, this
:class:`gil_scoped_release` appear: if there is any way that the C++ code
can access Python objects, :class:`gil_scoped_acquire` should be used to
reacquire the GIL. Taking :ref:`overriding_virtuals` as an example, this
could be realized as follows (important changes highlighted): could be realized as follows (important changes highlighted):
.. code-block:: cpp .. code-block:: cpp
:emphasize-lines: 8,30,31 :emphasize-lines: 8,9,31,32
class PyAnimal : public Animal { class PyAnimal : public Animal {
public: public:
@ -83,7 +56,9 @@ could be realized as follows (important changes highlighted):
/* Trampoline (need one for each virtual function) */ /* Trampoline (need one for each virtual function) */
std::string go(int n_times) { std::string go(int n_times) {
/* PYBIND11_OVERRIDE_PURE will acquire the GIL before accessing Python state */ /* Acquire GIL before calling Python code */
py::gil_scoped_acquire acquire;
PYBIND11_OVERRIDE_PURE( PYBIND11_OVERRIDE_PURE(
std::string, /* Return type */ std::string, /* Return type */
Animal, /* Parent class */ Animal, /* Parent class */
@ -103,14 +78,13 @@ could be realized as follows (important changes highlighted):
.def(py::init<>()); .def(py::init<>());
m.def("call_go", [](Animal *animal) -> std::string { m.def("call_go", [](Animal *animal) -> std::string {
// GIL is held when called from Python code. Release GIL before /* Release GIL before calling into (potentially long-running) C++ code */
// calling into (potentially long-running) C++ code
py::gil_scoped_release release; py::gil_scoped_release release;
return call_go(animal); return call_go(animal);
}); });
} }
The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy The ``call_go`` wrapper can also be simplified using the `call_guard` policy
(see :ref:`call_policies`) which yields the same result: (see :ref:`call_policies`) which yields the same result:
.. code-block:: cpp .. code-block:: cpp
@ -118,34 +92,6 @@ The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy
m.def("call_go", &call_go, py::call_guard<py::gil_scoped_release>()); m.def("call_go", &call_go, py::call_guard<py::gil_scoped_release>());
Common Sources Of Global Interpreter Lock Errors
==================================================================
Failing to properly hold the Global Interpreter Lock (GIL) is one of the
more common sources of bugs within code that uses pybind11. If you are
running into GIL related errors, we highly recommend you consult the
following checklist.
- Do you have any global variables that are pybind11 objects or invoke
pybind11 functions in either their constructor or destructor? You are generally
not allowed to invoke any Python function in a global static context. We recommend
using lazy initialization and then intentionally leaking at the end of the program.
- Do you have any pybind11 objects that are members of other C++ structures? One
commonly overlooked requirement is that pybind11 objects have to increase their reference count
whenever their copy constructor is called. Thus, you need to be holding the GIL to invoke
the copy constructor of any C++ class that has a pybind11 member. This can sometimes be very
tricky to track for complicated programs Think carefully when you make a pybind11 object
a member in another struct.
- C++ destructors that invoke Python functions can be particularly troublesome as
destructors can sometimes get invoked in weird and unexpected circumstances as a result
of exceptions.
- You should try running your code in a debug build. That will enable additional assertions
within pybind11 that will throw exceptions on certain GIL handling errors
(reference counting operations).
Binding sequence data types, iterators, the slicing protocol, etc. Binding sequence data types, iterators, the slicing protocol, etc.
================================================================== ==================================================================
@ -352,15 +298,6 @@ The class ``options`` allows you to selectively suppress auto-generated signatur
m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers"); m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers");
} }
pybind11 also appends all members of an enum to the resulting enum docstring.
This default behavior can be disabled by using the ``disable_enum_members_docstring()``
function of the ``options`` class.
With ``disable_user_defined_docstrings()`` all user defined docstrings of
``module_::def()``, ``class_::def()`` and ``enum_()`` are disabled, but the
function signatures and enum members are included in the docstring, unless they
are disabled separately.
Note that changes to the settings affect only function bindings created during the Note that changes to the settings affect only function bindings created during the
lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function, lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function,
the default settings are restored to prevent unwanted side effects. the default settings are restored to prevent unwanted side effects.
@ -398,32 +335,3 @@ before they are used as a parameter or return type of a function:
pyFoo.def(py::init<const ns::Bar&>()); pyFoo.def(py::init<const ns::Bar&>());
pyBar.def(py::init<const ns::Foo&>()); pyBar.def(py::init<const ns::Foo&>());
} }
Setting inner type hints in docstrings
======================================
When you use pybind11 wrappers for ``list``, ``dict``, and other generic python
types, the docstring will just display the generic type. You can convey the
inner types in the docstring by using a special 'typed' version of the generic
type.
.. code-block:: cpp
PYBIND11_MODULE(example, m) {
m.def("pass_list_of_str", [](py::typing::List<py::str> arg) {
// arg can be used just like py::list
));
}
The resulting docstring will be ``pass_list_of_str(arg0: list[str]) -> None``.
The following special types are available in ``pybind11/typing.h``:
* ``py::Tuple<Args...>``
* ``py::Dict<K, V>``
* ``py::List<V>``
* ``py::Set<V>``
* ``py::Callable<Signature>``
.. warning:: Just like in python, these are merely hints. They don't actually
enforce the types of their contents at runtime or compile time.

View File

@ -87,7 +87,7 @@ buffer objects (e.g. a NumPy matrix).
/* Request a buffer descriptor from Python */ /* Request a buffer descriptor from Python */
py::buffer_info info = b.request(); py::buffer_info info = b.request();
/* Some basic validation checks ... */ /* Some sanity checks ... */
if (info.format != py::format_descriptor<Scalar>::format()) if (info.format != py::format_descriptor<Scalar>::format())
throw std::runtime_error("Incompatible format: expected a double array!"); throw std::runtime_error("Incompatible format: expected a double array!");
@ -150,10 +150,8 @@ NumPy array containing double precision values.
When it is invoked with a different type (e.g. an integer or a list of When it is invoked with a different type (e.g. an integer or a list of
integers), the binding code will attempt to cast the input into a NumPy array integers), the binding code will attempt to cast the input into a NumPy array
of the requested type. This feature requires the :file:`pybind11/numpy.h` of the requested type. Note that this feature requires the
header to be included. Note that :file:`pybind11/numpy.h` does not depend on :file:`pybind11/numpy.h` header to be included.
the NumPy headers, and thus can be used without declaring a build-time
dependency on NumPy; NumPy>=1.7.0 is a runtime dependency.
Data in NumPy arrays is not guaranteed to packed in a dense manner; Data in NumPy arrays is not guaranteed to packed in a dense manner;
furthermore, entries can be separated by arbitrary column and row strides. furthermore, entries can be separated by arbitrary column and row strides.
@ -171,31 +169,6 @@ template parameter, and it ensures that non-conforming arguments are converted
into an array satisfying the specified requirements instead of trying the next into an array satisfying the specified requirements instead of trying the next
function overload. function overload.
There are several methods on arrays; the methods listed below under references
work, as well as the following functions based on the NumPy API:
- ``.dtype()`` returns the type of the contained values.
- ``.strides()`` returns a pointer to the strides of the array (optionally pass
an integer axis to get a number).
- ``.flags()`` returns the flag settings. ``.writable()`` and ``.owndata()``
are directly available.
- ``.offset_at()`` returns the offset (optionally pass indices).
- ``.squeeze()`` returns a view with length-1 axes removed.
- ``.view(dtype)`` returns a view of the array with a different dtype.
- ``.reshape({i, j, ...})`` returns a view of the array with a different shape.
``.resize({...})`` is also available.
- ``.index_at(i, j, ...)`` gets the count from the beginning to a given index.
There are also several methods for getting references (described below).
Structured types Structured types
================ ================
@ -258,8 +231,8 @@ by the compiler. The result is returned as a NumPy array of type
.. code-block:: pycon .. code-block:: pycon
>>> x = np.array([[1, 3], [5, 7]]) >>> x = np.array([[1, 3],[5, 7]])
>>> y = np.array([[2, 4], [6, 8]]) >>> y = np.array([[2, 4],[6, 8]])
>>> z = 3 >>> z = 3
>>> result = vectorized_func(x, y, z) >>> result = vectorized_func(x, y, z)
@ -370,21 +343,21 @@ The returned proxy object supports some of the same methods as ``py::array`` so
that it can be used as a drop-in replacement for some existing, index-checked that it can be used as a drop-in replacement for some existing, index-checked
uses of ``py::array``: uses of ``py::array``:
- ``.ndim()`` returns the number of dimensions - ``r.ndim()`` returns the number of dimensions
- ``.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to - ``r.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to
the ``const T`` or ``T`` data, respectively, at the given indices. The the ``const T`` or ``T`` data, respectively, at the given indices. The
latter is only available to proxies obtained via ``a.mutable_unchecked()``. latter is only available to proxies obtained via ``a.mutable_unchecked()``.
- ``.itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``. - ``itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``.
- ``.ndim()`` returns the number of dimensions. - ``ndim()`` returns the number of dimensions.
- ``.shape(n)`` returns the size of dimension ``n`` - ``shape(n)`` returns the size of dimension ``n``
- ``.size()`` returns the total number of elements (i.e. the product of the shapes). - ``size()`` returns the total number of elements (i.e. the product of the shapes).
- ``.nbytes()`` returns the number of bytes used by the referenced elements - ``nbytes()`` returns the number of bytes used by the referenced elements
(i.e. ``itemsize()`` times ``size()``). (i.e. ``itemsize()`` times ``size()``).
.. seealso:: .. seealso::
@ -395,13 +368,15 @@ uses of ``py::array``:
Ellipsis Ellipsis
======== ========
Python provides a convenient ``...`` ellipsis notation that is often used to Python 3 provides a convenient ``...`` ellipsis notation that is often used to
slice multidimensional arrays. For instance, the following snippet extracts the slice multidimensional arrays. For instance, the following snippet extracts the
middle dimensions of a tensor with the first and last index set to zero. middle dimensions of a tensor with the first and last index set to zero.
In Python 2, the syntactic sugar ``...`` is not available, but the singleton
``Ellipsis`` (of type ``ellipsis``) can still be used directly.
.. code-block:: python .. code-block:: python
a = ... # a NumPy array a = # a NumPy array
b = a[0, ..., 0] b = a[0, ..., 0]
The function ``py::ellipsis()`` function can be used to perform the same The function ``py::ellipsis()`` function can be used to perform the same
@ -412,6 +387,8 @@ operation on the C++ side:
py::array a = /* A NumPy array */; py::array a = /* A NumPy array */;
py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; py::array b = a[py::make_tuple(0, py::ellipsis(), 0)];
.. versionchanged:: 2.6
``py::ellipsis()`` is now also avaliable in Python 2.
Memory view Memory view
=========== ===========
@ -433,7 +410,7 @@ following:
{ 2, 4 }, // shape (rows, cols) { 2, 4 }, // shape (rows, cols)
{ sizeof(uint8_t) * 4, sizeof(uint8_t) } // strides in bytes { sizeof(uint8_t) * 4, sizeof(uint8_t) } // strides in bytes
); );
}); })
This approach is meant for providing a ``memoryview`` for a C/C++ buffer not This approach is meant for providing a ``memoryview`` for a C/C++ buffer not
managed by Python. The user is responsible for managing the lifetime of the managed by Python. The user is responsible for managing the lifetime of the
@ -449,7 +426,11 @@ We can also use ``memoryview::from_memory`` for a simple 1D contiguous buffer:
buffer, // buffer pointer buffer, // buffer pointer
sizeof(uint8_t) * 8 // buffer size sizeof(uint8_t) * 8 // buffer size
); );
}); })
.. note::
``memoryview::from_memory`` is not available in Python 2.
.. versionchanged:: 2.6 .. versionchanged:: 2.6
``memoryview::from_memory`` added. ``memoryview::from_memory`` added.

View File

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

View File

@ -28,7 +28,7 @@ Capturing standard output from ostream
Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print, Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print,
but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr`` but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr``
redirection. Replacing a library's printing with ``py::print <print>`` may not redirection. Replacing a library's printing with `py::print <print>` may not
be feasible. This can be fixed using a guard around the library function that be feasible. This can be fixed using a guard around the library function that
redirects output to the corresponding Python streams: redirects output to the corresponding Python streams:
@ -47,26 +47,15 @@ redirects output to the corresponding Python streams:
call_noisy_func(); call_noisy_func();
}); });
.. warning::
The implementation in ``pybind11/iostream.h`` is NOT thread safe. Multiple
threads writing to a redirected ostream concurrently cause data races
and potentially buffer overflows. Therefore it is currently a requirement
that all (possibly) concurrent redirected ostream writes are protected by
a mutex. #HelpAppreciated: Work on iostream.h thread safety. For more
background see the discussions under
`PR #2982 <https://github.com/pybind/pybind11/pull/2982>`_ and
`PR #2995 <https://github.com/pybind/pybind11/pull/2995>`_.
This method respects flushes on the output streams and will flush if needed This method respects flushes on the output streams and will flush if needed
when the scoped guard is destroyed. This allows the output to be redirected in when the scoped guard is destroyed. This allows the output to be redirected in
real time, such as to a Jupyter notebook. The two arguments, the C++ stream and real time, such as to a Jupyter notebook. The two arguments, the C++ stream and
the Python output, are optional, and default to standard output if not given. An the Python output, are optional, and default to standard output if not given. An
extra type, ``py::scoped_estream_redirect <scoped_estream_redirect>``, is identical extra type, `py::scoped_estream_redirect <scoped_estream_redirect>`, is identical
except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with
``py::call_guard``, which allows multiple items, but uses the default constructor: `py::call_guard`, which allows multiple items, but uses the default constructor:
.. code-block:: cpp .. code-block:: py
// Alternative: Call single function using call guard // Alternative: Call single function using call guard
m.def("noisy_func", &call_noisy_function, m.def("noisy_func", &call_noisy_function,
@ -74,7 +63,7 @@ except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful wi
py::scoped_estream_redirect>()); py::scoped_estream_redirect>());
The redirection can also be done in Python with the addition of a context The redirection can also be done in Python with the addition of a context
manager, using the ``py::add_ostream_redirect() <add_ostream_redirect>`` function: manager, using the `py::add_ostream_redirect() <add_ostream_redirect>` function:
.. code-block:: cpp .. code-block:: cpp
@ -103,7 +92,7 @@ arguments to disable one of the streams if needed.
Evaluating Python expressions from strings and files Evaluating Python expressions from strings and files
==================================================== ====================================================
pybind11 provides the ``eval``, ``exec`` and ``eval_file`` functions to evaluate pybind11 provides the `eval`, `exec` and `eval_file` functions to evaluate
Python expressions and statements. The following example illustrates how they Python expressions and statements. The following example illustrates how they
can be used. can be used.

View File

@ -77,7 +77,6 @@ segmentation fault).
.. code-block:: python .. code-block:: python
from example import Parent from example import Parent
print(Parent().get_child()) print(Parent().get_child())
The problem is that ``Parent::get_child()`` returns a pointer to an instance of The problem is that ``Parent::get_child()`` returns a pointer to an instance of
@ -157,7 +156,7 @@ specialized:
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>);
// Only needed if the type's `.get()` goes by another name // Only needed if the type's `.get()` goes by another name
namespace PYBIND11_NAMESPACE { namespace detail { namespace pybind11 { namespace detail {
template <typename T> template <typename T>
struct holder_helper<SmartPtr<T>> { // <-- specialization struct holder_helper<SmartPtr<T>> { // <-- specialization
static const T *get(const SmartPtr<T> &p) { return p.getPointer(); } static const T *get(const SmartPtr<T> &p) { return p.getPointer(); }

View File

@ -32,7 +32,8 @@ The last line will both compile and run the tests.
Windows Windows
------- -------
On Windows, only **Visual Studio 2017** and newer are supported. On Windows, only **Visual Studio 2015** and newer are supported since pybind11 relies
on various C++11 language features that break older versions of Visual Studio.
.. Note:: .. Note::
@ -108,7 +109,7 @@ a file named :file:`example.cpp` with the following contents:
PYBIND11_MODULE(example, m) { PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function that adds two numbers"); m.def("add", &add, "A function which adds two numbers");
} }
.. [#f1] In practice, implementation and binding code will generally be located .. [#f1] In practice, implementation and binding code will generally be located
@ -135,14 +136,7 @@ On Linux, the above example can be compiled using the following command:
.. code-block:: bash .. code-block:: bash
$ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) $ c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix`
.. note::
If you used :ref:`include_as_a_submodule` to get the pybind11 source, then
use ``$(python3-config --includes) -Iextern/pybind11/include`` instead of
``$(python3 -m pybind11 --includes)`` in the above compilation, as
explained in :ref:`building_manually`.
For more details on the required compiler flags on Linux and macOS, see For more details on the required compiler flags on Linux and macOS, see
:ref:`building_manually`. For complete cross-platform compilation instructions, :ref:`building_manually`. For complete cross-platform compilation instructions,
@ -165,12 +159,12 @@ load and execute the example:
.. code-block:: pycon .. code-block:: pycon
$ python $ python
Python 3.9.10 (main, Jan 15 2022, 11:48:04) Python 2.7.10 (default, Aug 22 2015, 20:33:39)
[Clang 13.0.0 (clang-1300.0.29.3)] on darwin [GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information. Type "help", "copyright", "credits" or "license" for more information.
>>> import example >>> import example
>>> example.add(1, 2) >>> example.add(1, 2)
3 3L
>>> >>>
.. _keyword_args: .. _keyword_args:

View File

@ -1,6 +1,8 @@
import datetime as dt # -*- coding: utf-8 -*-
import os
import random import random
import os
import time
import datetime as dt
nfns = 4 # Functions per class nfns = 4 # Functions per class
nargs = 4 # Arguments per function nargs = 4 # Arguments per function
@ -11,20 +13,20 @@ def generate_dummy_code_pybind11(nclasses=10):
bindings = "" bindings = ""
for cl in range(nclasses): for cl in range(nclasses):
decl += f"class cl{cl:03};\n" decl += "class cl%03i;\n" % cl
decl += "\n" decl += "\n"
for cl in range(nclasses): for cl in range(nclasses):
decl += f"class {cl:03} {{\n" decl += "class cl%03i {\n" % cl
decl += "public:\n" decl += "public:\n"
bindings += f' py::class_<cl{cl:03}>(m, "cl{cl:03}")\n' bindings += ' py::class_<cl%03i>(m, "cl%03i")\n' % (cl, cl)
for fn in range(nfns): for fn in range(nfns):
ret = random.randint(0, nclasses - 1) ret = random.randint(0, nclasses - 1)
params = [random.randint(0, nclasses - 1) for i in range(nargs)] params = [random.randint(0, nclasses - 1) for i in range(nargs)]
decl += f" cl{ret:03} *fn_{fn:03}(" decl += " cl%03i *fn_%03i(" % (ret, fn)
decl += ", ".join(f"cl{p:03} *" for p in params) decl += ", ".join("cl%03i *" % p for p in params)
decl += ");\n" decl += ");\n"
bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03})\n' bindings += ' .def("fn_%03i", &cl%03i::fn_%03i)\n' % (fn, cl, fn)
decl += "};\n\n" decl += "};\n\n"
bindings += " ;\n" bindings += " ;\n"
@ -42,20 +44,23 @@ def generate_dummy_code_boost(nclasses=10):
bindings = "" bindings = ""
for cl in range(nclasses): for cl in range(nclasses):
decl += f"class cl{cl:03};\n" decl += "class cl%03i;\n" % cl
decl += "\n" decl += "\n"
for cl in range(nclasses): for cl in range(nclasses):
decl += "class cl%03i {\n" % cl decl += "class cl%03i {\n" % cl
decl += "public:\n" decl += "public:\n"
bindings += f' py::class_<cl{cl:03}>("cl{cl:03}")\n' bindings += ' py::class_<cl%03i>("cl%03i")\n' % (cl, cl)
for fn in range(nfns): for fn in range(nfns):
ret = random.randint(0, nclasses - 1) ret = random.randint(0, nclasses - 1)
params = [random.randint(0, nclasses - 1) for i in range(nargs)] params = [random.randint(0, nclasses - 1) for i in range(nargs)]
decl += f" cl{ret:03} *fn_{fn:03}(" decl += " cl%03i *fn_%03i(" % (ret, fn)
decl += ", ".join(f"cl{p:03} *" for p in params) decl += ", ".join("cl%03i *" % p for p in params)
decl += ");\n" decl += ");\n"
bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy<py::manage_new_object>())\n' bindings += (
' .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy<py::manage_new_object>())\n'
% (fn, cl, fn)
)
decl += "};\n\n" decl += "};\n\n"
bindings += " ;\n" bindings += " ;\n"
@ -70,8 +75,8 @@ def generate_dummy_code_boost(nclasses=10):
for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]: for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]:
print("{") print("{")
for i in range(10): for i in range(0, 10):
nclasses = 2**i nclasses = 2 ** i
with open("test.cpp", "w") as f: with open("test.cpp", "w") as f:
f.write(codegen(nclasses)) f.write(codegen(nclasses))
n1 = dt.datetime.now() n1 = dt.datetime.now()

File diff suppressed because it is too large Load Diff

View File

@ -44,30 +44,20 @@ interactive Python session demonstrating this example is shown below:
% python % python
>>> import example >>> import example
>>> p = example.Pet("Molly") >>> p = example.Pet('Molly')
>>> print(p) >>> print(p)
<example.Pet object at 0x10cd98060> <example.Pet object at 0x10cd98060>
>>> p.getName() >>> p.getName()
'Molly' u'Molly'
>>> p.setName("Charly") >>> p.setName('Charly')
>>> p.getName() >>> p.getName()
'Charly' u'Charly'
.. seealso:: .. seealso::
Static member functions can be bound in the same way using Static member functions can be bound in the same way using
:func:`class_::def_static`. :func:`class_::def_static`.
.. note::
Binding C++ types in unnamed namespaces (also known as anonymous namespaces)
works reliably on many platforms, but not all. The `XFAIL_CONDITION` in
tests/test_unnamed_namespace_a.py encodes the currently known conditions.
For background see `#4319 <https://github.com/pybind/pybind11/pull/4319>`_.
If portability is a concern, it is therefore not recommended to bind C++
types in unnamed namespaces. It will be safest to manually pick unique
namespace names.
Keyword and default arguments Keyword and default arguments
============================= =============================
It is possible to specify keyword and default arguments using the syntax It is possible to specify keyword and default arguments using the syntax
@ -132,12 +122,12 @@ This makes it possible to write
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Pet("Molly") >>> p = example.Pet('Molly')
>>> p.name >>> p.name
'Molly' u'Molly'
>>> p.name = "Charly" >>> p.name = 'Charly'
>>> p.name >>> p.name
'Charly' u'Charly'
Now suppose that ``Pet::name`` was a private internal variable Now suppose that ``Pet::name`` was a private internal variable
that can only be accessed via setters and getters. that can only be accessed via setters and getters.
@ -184,10 +174,10 @@ Native Python classes can pick up new attributes dynamically:
.. code-block:: pycon .. code-block:: pycon
>>> class Pet: >>> class Pet:
... name = "Molly" ... name = 'Molly'
... ...
>>> p = Pet() >>> p = Pet()
>>> p.name = "Charly" # overwrite existing >>> p.name = 'Charly' # overwrite existing
>>> p.age = 2 # dynamically add a new attribute >>> p.age = 2 # dynamically add a new attribute
By default, classes exported from C++ do not support this and the only writable By default, classes exported from C++ do not support this and the only writable
@ -205,7 +195,7 @@ Trying to set any other attribute results in an error:
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Pet() >>> p = example.Pet()
>>> p.name = "Charly" # OK, attribute defined in C++ >>> p.name = 'Charly' # OK, attribute defined in C++
>>> p.age = 2 # fail >>> p.age = 2 # fail
AttributeError: 'Pet' object has no attribute 'age' AttributeError: 'Pet' object has no attribute 'age'
@ -223,7 +213,7 @@ Now everything works as expected:
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Pet() >>> p = example.Pet()
>>> p.name = "Charly" # OK, overwrite value in C++ >>> p.name = 'Charly' # OK, overwrite value in C++
>>> p.age = 2 # OK, dynamically add a new attribute >>> p.age = 2 # OK, dynamically add a new attribute
>>> p.__dict__ # just like a native Python class >>> p.__dict__ # just like a native Python class
{'age': 2} {'age': 2}
@ -290,11 +280,11 @@ expose fields and methods of both types:
.. code-block:: pycon .. code-block:: pycon
>>> p = example.Dog("Molly") >>> p = example.Dog('Molly')
>>> p.name >>> p.name
'Molly' u'Molly'
>>> p.bark() >>> p.bark()
'woof!' u'woof!'
The C++ classes defined above are regular non-polymorphic types with an The C++ classes defined above are regular non-polymorphic types with an
inheritance relationship. This is reflected in Python: inheritance relationship. This is reflected in Python:
@ -342,7 +332,7 @@ will automatically recognize this:
>>> type(p) >>> type(p)
PolymorphicDog # automatically downcast PolymorphicDog # automatically downcast
>>> p.bark() >>> p.bark()
'woof!' u'woof!'
Given a pointer to a polymorphic base, pybind11 performs automatic downcasting Given a pointer to a polymorphic base, pybind11 performs automatic downcasting
to the actual derived type. Note that this goes beyond the usual situation in to the actual derived type. Note that this goes beyond the usual situation in
@ -444,7 +434,8 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth
.def("set", overload_cast_<int>()(&Pet::set), "Set the pet's age") .def("set", overload_cast_<int>()(&Pet::set), "Set the pet's age")
.def("set", overload_cast_<const std::string &>()(&Pet::set), "Set the pet's name"); .def("set", overload_cast_<const std::string &>()(&Pet::set), "Set the pet's name");
.. [#cpp14] A compiler which supports the ``-std=c++14`` flag. .. [#cpp14] A compiler which supports the ``-std=c++14`` flag
or Visual Studio 2015 Update 2 and newer.
.. note:: .. note::
@ -455,7 +446,8 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth
Enumerations and internal types Enumerations and internal types
=============================== ===============================
Let's now suppose that the example class contains internal types like enumerations, e.g.: Let's now suppose that the example class contains an internal enumeration type,
e.g.:
.. code-block:: cpp .. code-block:: cpp
@ -465,15 +457,10 @@ Let's now suppose that the example class contains internal types like enumeratio
Cat Cat
}; };
struct Attributes {
float age = 0;
};
Pet(const std::string &name, Kind type) : name(name), type(type) { } Pet(const std::string &name, Kind type) : name(name), type(type) { }
std::string name; std::string name;
Kind type; Kind type;
Attributes attr;
}; };
The binding code for this example looks as follows: The binding code for this example looks as follows:
@ -484,28 +471,22 @@ The binding code for this example looks as follows:
pet.def(py::init<const std::string &, Pet::Kind>()) pet.def(py::init<const std::string &, Pet::Kind>())
.def_readwrite("name", &Pet::name) .def_readwrite("name", &Pet::name)
.def_readwrite("type", &Pet::type) .def_readwrite("type", &Pet::type);
.def_readwrite("attr", &Pet::attr);
py::enum_<Pet::Kind>(pet, "Kind") py::enum_<Pet::Kind>(pet, "Kind")
.value("Dog", Pet::Kind::Dog) .value("Dog", Pet::Kind::Dog)
.value("Cat", Pet::Kind::Cat) .value("Cat", Pet::Kind::Cat)
.export_values(); .export_values();
py::class_<Pet::Attributes>(pet, "Attributes") To ensure that the ``Kind`` type is created within the scope of ``Pet``, the
.def(py::init<>()) ``pet`` :class:`class_` instance must be supplied to the :class:`enum_`.
.def_readwrite("age", &Pet::Attributes::age);
To ensure that the nested types ``Kind`` and ``Attributes`` are created within the scope of ``Pet``, the
``pet`` :class:`class_` instance must be supplied to the :class:`enum_` and :class:`class_`
constructor. The :func:`enum_::export_values` function exports the enum entries constructor. The :func:`enum_::export_values` function exports the enum entries
into the parent scope, which should be skipped for newer C++11-style strongly into the parent scope, which should be skipped for newer C++11-style strongly
typed enums. typed enums.
.. code-block:: pycon .. code-block:: pycon
>>> p = Pet("Lucy", Pet.Cat) >>> p = Pet('Lucy', Pet.Cat)
>>> p.type >>> p.type
Kind.Cat Kind.Cat
>>> int(p.type) >>> int(p.type)
@ -527,7 +508,7 @@ The ``name`` property returns the name of the enum value as a unicode string.
.. code-block:: pycon .. code-block:: pycon
>>> p = Pet("Lucy", Pet.Cat) >>> p = Pet( "Lucy", Pet.Cat )
>>> pet_type = p.type >>> pet_type = p.type
>>> pet_type >>> pet_type
Pet.Cat Pet.Cat
@ -549,7 +530,3 @@ The ``name`` property returns the name of the enum value as a unicode string.
... ...
By default, these are omitted to conserve space. By default, these are omitted to conserve space.
.. warning::
Contrary to Python customs, enum values from the wrappers should not be compared using ``is``, but with ``==`` (see `#1177 <https://github.com/pybind/pybind11/issues/1177>`_ for background).

View File

@ -42,7 +42,10 @@ An example of a ``setup.py`` using pybind11's helpers:
), ),
] ]
setup(..., ext_modules=ext_modules) setup(
...,
ext_modules=ext_modules
)
If you want to do an automatic search for the highest supported C++ standard, If you want to do an automatic search for the highest supported C++ standard,
that is supported via a ``build_ext`` command override; it will only affect that is supported via a ``build_ext`` command override; it will only affect
@ -61,20 +64,11 @@ that is supported via a ``build_ext`` command override; it will only affect
), ),
] ]
setup(..., cmdclass={"build_ext": build_ext}, ext_modules=ext_modules) setup(
...,
If you have single-file extension modules that are directly stored in the cmdclass={"build_ext": build_ext},
Python source tree (``foo.cpp`` in the same directory as where a ``foo.py`` ext_modules=ext_modules
would be located), you can also generate ``Pybind11Extensions`` using )
``setup_helpers.intree_extensions``: ``intree_extensions(["path/to/foo.cpp",
...])`` returns a list of ``Pybind11Extensions`` which can be passed to
``ext_modules``, possibly after further customizing their attributes
(``libraries``, ``include_dirs``, etc.). By doing so, a ``foo.*.so`` extension
module will be generated and made available upon installation.
``intree_extension`` will automatically detect if you are using a ``src``-style
layout (as long as no namespace packages are involved), but you can also
explicitly pass ``package_dir`` to it (as in ``setuptools.setup``).
Since pybind11 does not require NumPy when building, a light-weight replacement Since pybind11 does not require NumPy when building, a light-weight replacement
for NumPy's parallel compilation distutils tool is included. Use it like this: for NumPy's parallel compilation distutils tool is included. Use it like this:
@ -90,23 +84,22 @@ for NumPy's parallel compilation distutils tool is included. Use it like this:
The argument is the name of an environment variable to control the number of The argument is the name of an environment variable to control the number of
threads, such as ``NPY_NUM_BUILD_JOBS`` (as used by NumPy), though you can set threads, such as ``NPY_NUM_BUILD_JOBS`` (as used by NumPy), though you can set
something different if you want; ``CMAKE_BUILD_PARALLEL_LEVEL`` is another choice something different if you want. You can also pass ``default=N`` to set the
a user might expect. You can also pass ``default=N`` to set the default number default number of threads (0 will take the number of threads available) and
of threads (0 will take the number of threads available) and ``max=N``, the ``max=N``, the maximum number of threads; if you have a large extension you may
maximum number of threads; if you have a large extension you may want set this want set this to a memory dependent number.
to a memory dependent number.
If you are developing rapidly and have a lot of C++ files, you may want to If you are developing rapidly and have a lot of C++ files, you may want to
avoid rebuilding files that have not changed. For simple cases were you are avoid rebuilding files that have not changed. For simple cases were you are
using ``pip install -e .`` and do not have local headers, you can skip the using ``pip install -e .`` and do not have local headers, you can skip the
rebuild if an object file is newer than its source (headers are not checked!) rebuild if a object file is newer than it's source (headers are not checked!)
with the following: with the following:
.. code-block:: python .. code-block:: python
from pybind11.setup_helpers import ParallelCompile, naive_recompile from pybind11.setup_helpers import ParallelCompile, naive_recompile
ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() SmartCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install()
If you have a more complex build, you can implement a smarter function and pass If you have a more complex build, you can implement a smarter function and pass
@ -143,7 +136,7 @@ Your ``pyproject.toml`` file will likely look something like this:
.. code-block:: toml .. code-block:: toml
[build-system] [build-system]
requires = ["setuptools>=42", "pybind11>=2.6.1"] requires = ["setuptools", "wheel", "pybind11==2.6.0"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
.. note:: .. note::
@ -154,12 +147,10 @@ Your ``pyproject.toml`` file will likely look something like this:
in Python) using something like `cibuildwheel`_, remember that ``setup.py`` in Python) using something like `cibuildwheel`_, remember that ``setup.py``
and ``pyproject.toml`` are not even contained in the wheel, so this high and ``pyproject.toml`` are not even contained in the wheel, so this high
Pip requirement is only for source builds, and will not affect users of Pip requirement is only for source builds, and will not affect users of
your binary wheels. If you are building SDists and wheels, then your binary wheels.
`pypa-build`_ is the recommended official tool.
.. _PEP 517: https://www.python.org/dev/peps/pep-0517/ .. _PEP 517: https://www.python.org/dev/peps/pep-0517/
.. _cibuildwheel: https://cibuildwheel.readthedocs.io .. _cibuildwheel: https://cibuildwheel.readthedocs.io
.. _pypa-build: https://pypa-build.readthedocs.io/en/latest/
.. _setup_helpers-setup_requires: .. _setup_helpers-setup_requires:
@ -241,7 +232,7 @@ extension module can be created with just a few lines of code:
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.27) cmake_minimum_required(VERSION 3.4...3.18)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
add_subdirectory(pybind11) add_subdirectory(pybind11)
@ -261,9 +252,6 @@ PyPI integration, can be found in the [cmake_example]_ repository.
.. versionchanged:: 2.6 .. versionchanged:: 2.6
CMake 3.4+ is required. CMake 3.4+ is required.
.. versionchanged:: 2.11
CMake 3.5+ is required.
Further information can be found at :doc:`cmake/index`. Further information can be found at :doc:`cmake/index`.
pybind11_add_module pybind11_add_module
@ -343,7 +331,7 @@ standard explicitly with
set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ version selection") # or 11, 14, 17, 20 set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ version selection") # or 11, 14, 17, 20
set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported
set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensions off set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensionsn off
The variables can also be set when calling CMake from the command line using The variables can also be set when calling CMake from the command line using
the ``-D<variable>=<value>`` flag. You can also manually set ``CXX_STANDARD`` the ``-D<variable>=<value>`` flag. You can also manually set ``CXX_STANDARD``
@ -413,17 +401,16 @@ can refer to the same [cmake_example]_ repository for a full sample project
FindPython mode FindPython mode
--------------- ---------------
CMake 3.12+ (3.15+ recommended, 3.18.2+ ideal) added a new module called CMake 3.12+ (3.15+ recommended) added a new module called FindPython that had a
FindPython that had a highly improved search algorithm and modern targets highly improved search algorithm and modern targets and tools. If you use
and tools. If you use FindPython, pybind11 will detect this and use the FindPython, pybind11 will detect this and use the existing targets instead:
existing targets instead:
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.15...3.22) cmake_minumum_required(VERSION 3.15...3.18)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(Python 3.6 COMPONENTS Interpreter Development REQUIRED) find_package(Python COMPONENTS Interpreter Development REQUIRED)
find_package(pybind11 CONFIG REQUIRED) find_package(pybind11 CONFIG REQUIRED)
# or add_subdirectory(pybind11) # or add_subdirectory(pybind11)
@ -436,8 +423,9 @@ algorithms from the CMake invocation, with ``-DPYBIND11_FINDPYTHON=ON``.
.. warning:: .. warning::
If you use FindPython to multi-target Python versions, use the individual If you use FindPython2 and FindPython3 to dual-target Python, use the
targets listed below, and avoid targets that directly include Python parts. individual targets listed below, and avoid targets that directly include
Python parts.
There are `many ways to hint or force a discovery of a specific Python There are `many ways to hint or force a discovery of a specific Python
installation <https://cmake.org/cmake/help/latest/module/FindPython.html>`_), installation <https://cmake.org/cmake/help/latest/module/FindPython.html>`_),
@ -445,14 +433,6 @@ setting ``Python_ROOT_DIR`` may be the most common one (though with
virtualenv/venv support, and Conda support, this tends to find the correct virtualenv/venv support, and Conda support, this tends to find the correct
Python version more often than the old system did). Python version more often than the old system did).
.. warning::
When the Python libraries (i.e. ``libpythonXX.a`` and ``libpythonXX.so``
on Unix) are not available, as is the case on a manylinux image, the
``Development`` component will not be resolved by ``FindPython``. When not
using the embedding functionality, CMake 3.18+ allows you to specify
``Development.Module`` instead of ``Development`` to resolve this issue.
.. versionadded:: 2.6 .. versionadded:: 2.6
Advanced: interface library targets Advanced: interface library targets
@ -464,8 +444,11 @@ available in all modes. The targets provided are:
``pybind11::headers`` ``pybind11::headers``
Just the pybind11 headers and minimum compile requirements Just the pybind11 headers and minimum compile requirements
``pybind11::python2_no_register``
Quiets the warning/error when mixing C++14 or higher and Python 2
``pybind11::pybind11`` ``pybind11::pybind11``
Python headers + ``pybind11::headers`` Python headers + ``pybind11::headers`` + ``pybind11::python2_no_register`` (Python 2 only)
``pybind11::python_link_helper`` ``pybind11::python_link_helper``
Just the "linking" part of pybind11:module Just the "linking" part of pybind11:module
@ -474,7 +457,7 @@ available in all modes. The targets provided are:
Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper`` Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper``
``pybind11::embed`` ``pybind11::embed``
Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Embed`` (FindPython) or Python libs
``pybind11::lto`` / ``pybind11::thin_lto`` ``pybind11::lto`` / ``pybind11::thin_lto``
An alternative to `INTERPROCEDURAL_OPTIMIZATION` for adding link-time optimization. An alternative to `INTERPROCEDURAL_OPTIMIZATION` for adding link-time optimization.
@ -498,7 +481,7 @@ You can use these targets to build complex applications. For example, the
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.27) cmake_minimum_required(VERSION 3.4)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
@ -508,10 +491,7 @@ You can use these targets to build complex applications. For example, the
target_link_libraries(example PRIVATE pybind11::module pybind11::lto pybind11::windows_extras) target_link_libraries(example PRIVATE pybind11::module pybind11::lto pybind11::windows_extras)
pybind11_extension(example) pybind11_extension(example)
if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) pybind11_strip(example)
# Strip unnecessary sections of the binary on Linux/macOS
pybind11_strip(example)
endif()
set_target_properties(example PROPERTIES CXX_VISIBILITY_PRESET "hidden" set_target_properties(example PROPERTIES CXX_VISIBILITY_PRESET "hidden"
CUDA_VISIBILITY_PRESET "hidden") CUDA_VISIBILITY_PRESET "hidden")
@ -524,7 +504,7 @@ Instead of setting properties, you can set ``CMAKE_*`` variables to initialize t
compiler flags are provided to ensure high quality code generation. In compiler flags are provided to ensure high quality code generation. In
contrast to the ``pybind11_add_module()`` command, the CMake interface contrast to the ``pybind11_add_module()`` command, the CMake interface
provides a *composable* set of targets to ensure that you retain flexibility. provides a *composable* set of targets to ensure that you retain flexibility.
It can be especially important to provide or set these properties; the It can be expecially important to provide or set these properties; the
:ref:`FAQ <faq:symhidden>` contains an explanation on why these are needed. :ref:`FAQ <faq:symhidden>` contains an explanation on why these are needed.
.. versionadded:: 2.6 .. versionadded:: 2.6
@ -556,7 +536,7 @@ information about usage in C++, see :doc:`/advanced/embedding`.
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.27) cmake_minimum_required(VERSION 3.4...3.18)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
@ -577,7 +557,10 @@ On Linux, you can compile an example such as the one given in
.. code-block:: bash .. code-block:: bash
$ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) $ c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix`
The flags given here assume that you're using Python 3. For Python 2, just
change the executable appropriately (to ``python`` or ``python2``).
The ``python3 -m pybind11 --includes`` command fetches the include paths for The ``python3 -m pybind11 --includes`` command fetches the include paths for
both pybind11 and Python headers. This assumes that pybind11 has been installed both pybind11 and Python headers. This assumes that pybind11 has been installed
@ -585,13 +568,19 @@ using ``pip`` or ``conda``. If it hasn't, you can also manually specify
``-I <path-to-pybind11>/include`` together with the Python includes path ``-I <path-to-pybind11>/include`` together with the Python includes path
``python3-config --includes``. ``python3-config --includes``.
Note that Python 2.7 modules don't use a special suffix, so you should simply
use ``example.so`` instead of ``example`python3-config --extension-suffix```.
Besides, the ``--extension-suffix`` option may or may not be available, depending
on the distribution; in the latter case, the module extension can be manually
set to ``.so``.
On macOS: the build command is almost the same but it also requires passing On macOS: the build command is almost the same but it also requires passing
the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when
building the module: building the module:
.. code-block:: bash .. code-block:: bash
$ c++ -O3 -Wall -shared -std=c++11 -undefined dynamic_lookup $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) $ c++ -O3 -Wall -shared -std=c++11 -undefined dynamic_lookup `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix`
In general, it is advisable to include several additional build parameters In general, it is advisable to include several additional build parameters
that can considerably reduce the size of the created binary. Refer to section that can considerably reduce the size of the created binary. Refer to section
@ -639,11 +628,3 @@ cross-project dependency management. Additionally, it is able to autogenerate
customizable pybind11-based wrappers by parsing C++ header files. customizable pybind11-based wrappers by parsing C++ header files.
.. [robotpy-build] https://robotpy-build.readthedocs.io .. [robotpy-build] https://robotpy-build.readthedocs.io
[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
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).
.. [litgen] https://pthom.github.io/litgen

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
# #
# pybind11 documentation build configuration file, created by # pybind11 documentation build configuration file, created by
# sphinx-quickstart on Sun Oct 11 19:23:48 2015. # sphinx-quickstart on Sun Oct 11 19:23:48 2015.
@ -12,11 +13,12 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
import os
import re
import subprocess
import sys import sys
import os
import shlex
import subprocess
from pathlib import Path from pathlib import Path
import re
DIR = Path(__file__).parent.resolve() DIR = Path(__file__).parent.resolve()
@ -35,7 +37,6 @@ DIR = Path(__file__).parent.resolve()
# ones. # ones.
extensions = [ extensions = [
"breathe", "breathe",
"sphinx_copybutton",
"sphinxcontrib.rsvgconverter", "sphinxcontrib.rsvgconverter",
"sphinxcontrib.moderncmakedomain", "sphinxcontrib.moderncmakedomain",
] ]
@ -126,7 +127,23 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
html_theme = "furo" on_rtd = os.environ.get("READTHEDOCS", None) == "True"
if not on_rtd: # only import and set the theme if we're building docs locally
import sphinx_rtd_theme
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_context = {"css_files": ["_static/theme_overrides.css"]}
else:
html_context = {
"css_files": [
"//media.readthedocs.org/css/sphinx_rtd_theme.css",
"//media.readthedocs.org/css/readthedocs-doc-embed.css",
"_static/theme_overrides.css",
]
}
# Theme options are theme-specific and customize the look and feel of a theme # Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the # further. For a list of options available for each theme, see the
@ -157,10 +174,6 @@ html_theme = "furo"
# so a file named "default.css" will overwrite the builtin "default.css". # so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"] html_static_path = ["_static"]
html_css_files = [
"css/custom.css",
]
# Add any extra paths that contain custom files (such as robots.txt or # Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied # .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation. # directly to the root of the documentation.
@ -226,8 +239,6 @@ htmlhelp_basename = "pybind11doc"
# -- Options for LaTeX output --------------------------------------------- # -- Options for LaTeX output ---------------------------------------------
latex_engine = "pdflatex"
latex_elements = { latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper', # 'papersize': 'letterpaper',
@ -333,31 +344,29 @@ def generate_doxygen_xml(app):
subprocess.call(["doxygen", "--version"]) subprocess.call(["doxygen", "--version"])
retcode = subprocess.call(["doxygen"], cwd=app.confdir) retcode = subprocess.call(["doxygen"], cwd=app.confdir)
if retcode < 0: if retcode < 0:
sys.stderr.write(f"doxygen error code: {-retcode}\n") sys.stderr.write("doxygen error code: {}\n".format(-retcode))
except OSError as e: except OSError as e:
sys.stderr.write(f"doxygen execution failed: {e}\n") sys.stderr.write("doxygen execution failed: {}\n".format(e))
def prepare(app): def prepare(app):
with open(DIR.parent / "README.rst") as f: with open(DIR.parent / "README.rst") as f:
contents = f.read() contents = f.read()
# Filter out section titles for index.rst for LaTeX
if app.builder.name == "latex": if app.builder.name == "latex":
# Remove badges and stuff from start
contents = contents[contents.find(r".. start") :]
# Filter out section titles for index.rst for LaTeX
contents = re.sub(r"^(.*)\n[-~]{3,}$", r"**\1**", contents, flags=re.MULTILINE) contents = re.sub(r"^(.*)\n[-~]{3,}$", r"**\1**", contents, flags=re.MULTILINE)
with open(DIR / "readme.rst", "w") as f: with open(DIR / "readme.rst", "w") as f:
f.write(contents) f.write(contents)
def clean_up(app, exception): # noqa: ARG001 def clean_up(app, exception):
(DIR / "readme.rst").unlink() (DIR / "readme.rst").unlink()
def setup(app): def setup(app):
# Add hook for building doxygen xml when needed # Add hook for building doxygen xml when needed
app.connect("builder-inited", generate_doxygen_xml) app.connect("builder-inited", generate_doxygen_xml)

View File

@ -5,10 +5,12 @@ Frequently asked questions
=========================================================== ===========================================================
1. Make sure that the name specified in PYBIND11_MODULE is identical to the 1. Make sure that the name specified in PYBIND11_MODULE is identical to the
filename of the extension library (without suffixes such as ``.so``). filename of the extension library (without suffixes such as .so)
2. If the above did not fix the issue, you are likely using an incompatible 2. If the above did not fix the issue, you are likely using an incompatible
version of Python that does not match what you compiled with. version of Python (for instance, the extension library was compiled against
Python 2, while the interpreter is running on top of some version of Python
3, or vice versa).
"Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``" "Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``"
======================================================================== ========================================================================
@ -52,7 +54,7 @@ provided by the caller -- in fact, it does nothing at all.
.. code-block:: python .. code-block:: python
def increment(i): def increment(i):
i += 1 # nope.. i += 1 # nope..
pybind11 is also affected by such language-level conventions, which means that pybind11 is also affected by such language-level conventions, which means that
binding ``increment`` or ``increment_ptr`` will also create Python functions binding ``increment`` or ``increment_ptr`` will also create Python functions
@ -145,7 +147,7 @@ using C++14 template metaprogramming.
.. _`faq:hidden_visibility`: .. _`faq:hidden_visibility`:
"'SomeClass' declared with greater visibility than the type of its field 'SomeClass::member' [-Wattributes]" "SomeClass declared with greater visibility than the type of its field SomeClass::member [-Wattributes]"
============================================================================================================ ============================================================================================================
This error typically indicates that you are compiling without the required This error typically indicates that you are compiling without the required
@ -167,8 +169,8 @@ can be changed, but even if it isn't it is not always enough to guarantee
complete independence of the symbols involved when not using complete independence of the symbols involved when not using
``-fvisibility=hidden``. ``-fvisibility=hidden``.
Additionally, ``-fvisibility=hidden`` can deliver considerably binary size Additionally, ``-fvisiblity=hidden`` can deliver considerably binary size
savings. (See the following section for more details.) savings. (See the following section for more details).
.. _`faq:symhidden`: .. _`faq:symhidden`:
@ -178,7 +180,7 @@ How can I create smaller binaries?
To do its job, pybind11 extensively relies on a programming technique known as To do its job, pybind11 extensively relies on a programming technique known as
*template metaprogramming*, which is a way of performing computation at compile *template metaprogramming*, which is a way of performing computation at compile
time using type information. Template metaprogramming usually instantiates code time using type information. Template metaprogamming usually instantiates code
involving significant numbers of deeply nested types that are either completely involving significant numbers of deeply nested types that are either completely
removed or reduced to just a few instructions during the compiler's optimization removed or reduced to just a few instructions during the compiler's optimization
phase. However, due to the nested nature of these types, the resulting symbol phase. However, due to the nested nature of these types, the resulting symbol
@ -220,6 +222,20 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids
potential serious issues when loading multiple modules and is required for potential serious issues when loading multiple modules and is required for
proper pybind operation. See the previous FAQ entry for more details. proper pybind operation. See the previous FAQ entry for more details.
Working with ancient Visual Studio 2008 builds on Windows
=========================================================
The official Windows distributions of Python are compiled using truly
ancient versions of Visual Studio that lack good C++11 support. Some users
implicitly assume that it would be impossible to load a plugin built with
Visual Studio 2015 into a Python distribution that was compiled using Visual
Studio 2008. However, no such issue exists: it's perfectly legitimate to
interface DLLs that are built with different compilers and/or C libraries.
Common gotchas to watch out for involve not ``free()``-ing memory region
that that were ``malloc()``-ed in another shared library, using data
structures with incompatible ABIs, and so on. pybind11 is very careful not
to make these types of mistakes.
How can I properly handle Ctrl-C in long-running functions? How can I properly handle Ctrl-C in long-running functions?
=========================================================== ===========================================================
@ -273,7 +289,27 @@ Conflicts can arise, however, when using pybind11 in a project that *also* uses
the CMake Python detection in a system with several Python versions installed. the CMake Python detection in a system with several Python versions installed.
This difference may cause inconsistencies and errors if *both* mechanisms are This difference may cause inconsistencies and errors if *both* mechanisms are
used in the same project. used in the same project. Consider the following CMake code executed in a
system with Python 2.7 and 3.x installed:
.. code-block:: cmake
find_package(PythonInterp)
find_package(PythonLibs)
find_package(pybind11)
It will detect Python 2.7 and pybind11 will pick it as well.
In contrast this code:
.. code-block:: cmake
find_package(pybind11)
find_package(PythonInterp)
find_package(PythonLibs)
will detect Python 3.x for pybind11 and may crash on
``find_package(PythonLibs)`` afterwards.
There are three possible solutions: There are three possible solutions:
@ -284,8 +320,7 @@ There are three possible solutions:
COMPONENTS Interpreter Development)`` on modern CMake (3.12+, 3.15+ better, 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 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 of the old, deprecated search tools, and these modules are much better at
finding the correct Python. If FindPythonLibs/Interp are not available finding the correct Python.
(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. 3. Set ``PYBIND11_NOPYTHON`` to ``TRUE``. Pybind11 will not search for Python.
However, you will have to use the target-based system, and do more setup However, you will have to use the target-based system, and do more setup
yourself, because it does not know about or include things that depend on yourself, because it does not know about or include things that depend on

View File

@ -8,8 +8,6 @@ There are several ways to get the pybind11 source, which lives at
developers recommend one of the first three ways listed here, submodule, PyPI, developers recommend one of the first three ways listed here, submodule, PyPI,
or conda-forge, for obtaining pybind11. or conda-forge, for obtaining pybind11.
.. _include_as_a_submodule:
Include as a submodule Include as a submodule
====================== ======================
@ -18,7 +16,7 @@ as a submodule. From your git repository, use:
.. code-block:: bash .. code-block:: bash
git submodule add -b stable ../../pybind/pybind11 extern/pybind11 git submodule add ../../pybind/pybind11 extern/pybind11 -b stable
git submodule update --init git submodule update --init
This assumes you are placing your dependencies in ``extern/``, and that you are This assumes you are placing your dependencies in ``extern/``, and that you are

View File

@ -57,16 +57,16 @@ clean, well written patch would likely be accepted to solve them.
Python 3.9.0 warning Python 3.9.0 warning
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
Combining older versions of pybind11 (< 2.6.0) with Python on exactly 3.9.0 Combining older versions of pybind11 (< 2.6.0) with Python on 3.9.0 will
will trigger undefined behavior that typically manifests as crashes during trigger undefined behavior that typically manifests as crashes during
interpreter shutdown (but could also destroy your data. **You have been interpreter shutdown (but could also destroy your data. **You have been
warned**). warned**).
This issue was `fixed in Python <https://github.com/python/cpython/pull/22670>`_. This issue has been
As a mitigation for this bug, pybind11 2.6.0 or newer includes a workaround `fixed in Python <https://github.com/python/cpython/pull/22670>`_. As a
specifically when Python 3.9.0 is detected at runtime, leaking about 50 bytes mitigation until 3.9.1 is released and commonly used, pybind11 (2.6.0 or newer)
of memory when a callback function is garbage collected. For reference, the includes a temporary workaround specifically when Python 3.9.0 is detected at
pybind11 test suite has about 2,000 such callbacks, but only 49 are garbage runtime, leaking about 50 bytes of memory when a callback function is garbage
collected before the end-of-process. Wheels (even if built with Python 3.9.0) collected. For reference; the pybind11 test suite has about 2,000 such
will correctly avoid the leak when run in Python 3.9.1, and this does not callbacks, but only 49 are garbage collected before the end-of-process. Wheels
affect other 3.X versions. built with Python 3.9.0 will correctly avoid the leak when run in Python 3.9.1.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -52,20 +52,6 @@ Convenience classes for specific Python types
.. doxygengroup:: pytypes .. doxygengroup:: pytypes
:members: :members:
Convenience functions converting to Python types
================================================
.. doxygenfunction:: make_tuple(Args&&...)
.. doxygenfunction:: make_iterator(Iterator, Sentinel, Extra &&...)
.. doxygenfunction:: make_iterator(Type &, Extra&&...)
.. doxygenfunction:: make_key_iterator(Iterator, Sentinel, Extra &&...)
.. doxygenfunction:: make_key_iterator(Type &, Extra&&...)
.. doxygenfunction:: make_value_iterator(Iterator, Sentinel, Extra &&...)
.. doxygenfunction:: make_value_iterator(Type &, Extra&&...)
.. _extras: .. _extras:
Passing extra arguments to ``def`` or ``class_`` Passing extra arguments to ``def`` or ``class_``
@ -124,6 +110,7 @@ Exceptions
.. doxygenclass:: builtin_exception .. doxygenclass:: builtin_exception
:members: :members:
Literals Literals
======== ========

View File

@ -15,129 +15,63 @@ For example:
For beta, ``PYBIND11_VERSION_PATCH`` should be ``Z.b1``. RC's can be ``Z.rc1``. For beta, ``PYBIND11_VERSION_PATCH`` should be ``Z.b1``. RC's can be ``Z.rc1``.
Always include the dot (even though PEP 440 allows it to be dropped). For a Always include the dot (even though PEP 440 allows it to be dropped). For a
final release, this must be a simple integer. There is also final release, this must be a simple integer.
``PYBIND11_VERSION_HEX`` just below that needs to be updated.
To release a new version of pybind11: To release a new version of pybind11:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you don't have nox, you should either use ``pipx run nox`` instead, or use
``pipx install nox`` or ``brew install nox`` (Unix).
- Update the version number - Update the version number
- Update ``PYBIND11_VERSION_MAJOR`` etc. in - Update ``PYBIND11_VERSION_MAJOR`` etc. in
``include/pybind11/detail/common.h``. PATCH should be a simple integer. ``include/pybind11/detail/common.h``. PATCH should be a simple integer.
- Update ``pybind11/_version.py`` (match above)
- Update ``PYBIND11_VERSION_HEX`` just below as well. - Ensure that all the information in ``setup.py`` is up-to-date.
- Add release date in ``docs/changelog.rst``.
- Update ``pybind11/_version.py`` (match above). - ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
- Ensure that all the information in ``setup.cfg`` is up-to-date, like
supported Python versions.
- Add release date in ``docs/changelog.rst`` and integrate the output of
``nox -s make_changelog``.
- Note that the ``nox -s make_changelog`` command inspects
`needs changelog <https://github.com/pybind/pybind11/pulls?q=is%3Apr+is%3Aclosed+label%3A%22needs+changelog%22>`_.
- Manually clear the ``needs changelog`` labels using the GitHub web
interface (very easy: start by clicking the link above).
- ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it
fails due to a known flake issue, either ignore or restart CI.) fails due to a known flake issue, either ignore or restart CI.)
- Add a release branch if this is a new minor version
- Add a release branch if this is a new MINOR version, or update the existing - ``git checkout -b vX.Y``, ``git push -u origin vX.Y``
release branch if it is a patch version
- New branch: ``git checkout -b vX.Y``, ``git push -u origin vX.Y``
- Update branch: ``git checkout vX.Y``, ``git merge <release branch>``, ``git push``
- Update tags (optional; if you skip this, the GitHub release makes a - Update tags (optional; if you skip this, the GitHub release makes a
non-annotated tag for you) non-annotated tag for you)
- ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``.
- ``git tag -a vX.Y.Z -m 'vX.Y.Z release'`` - ``git push --tags``.
- ``grep ^__version__ pybind11/_version.py``
- Last-minute consistency check: same as tag?
- ``git push --tags``
- Update stable - Update stable
- ``git checkout stable``
- ``git checkout stable`` - ``git merge master``
- ``git push``
- ``git merge -X theirs vX.Y.Z``
- ``git diff vX.Y.Z``
- Carefully review and reconcile any diffs. There should be none.
- ``git push``
- Make a GitHub release (this shows up in the UI, sends new release - Make a GitHub release (this shows up in the UI, sends new release
notifications to users watching releases, and also uploads PyPI packages). notifications to users watching releases, and also uploads PyPI packages).
(Note: if you do not use an existing tag, this creates a new lightweight tag (Note: if you do not use an existing tag, this creates a new lightweight tag
for you, so you could skip the above step.) for you, so you could skip the above step).
- GUI method: click "Create a new release" on the far right, fill in the tag
- GUI method: Under `releases <https://github.com/pybind/pybind11/releases>`_ name (if you didn't tag above, it will be made here), fill in a release
click "Draft a new release" on the far right, fill in the tag name name like "Version X.Y.Z", and optionally copy-and-paste the changelog into
(if you didn't tag above, it will be made here), fill in a release name the description (processed as markdown by Pandoc). Check "pre-release" if
like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog this is a beta/RC.
into the description. You can use ``cat docs/changelog.rst | pandoc -f rst -t gfm``,
then manually remove line breaks and strip links to PRs and issues,
e.g. to a bare ``#1234``, without the surrounding ``<...>_`` hyperlink markup.
Check "pre-release" if this is a beta/RC.
- CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"`` - CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"``
If this is a pre-release, add ``-p``. If this is a pre-release, add ``-p``.
- Get back to work - Get back to work
- Make sure you are on master, not somewhere else: ``git checkout master`` - Make sure you are on master, not somewhere else: ``git checkout master``
- Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to - Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to
``0.dev1`` and increment MINOR). ``0.dev1`` and increment MINOR).
- Update ``_version.py`` to match
- Update ``pybind11/_version.py`` to match. - Add a plot for in-development updates in ``docs/changelog.rst``.
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
- If the release was a new MINOR version, add a new ``IN DEVELOPMENT``
section in ``docs/changelog.rst``.
- ``git add``, ``git commit``, ``git push`` - ``git add``, ``git commit``, ``git push``
If a version branch is updated, remember to set PATCH to ``1.dev1``. If a version branch is updated, remember to set PATCH to ``1.dev1``.
If you'd like to bump homebrew, run:
.. code-block:: console
brew bump-formula-pr --url https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz
Conda-forge should automatically make a PR in a few hours, and automatically
merge it if there are no issues.
Manual packaging Manual packaging
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
If you need to manually upload releases, you can download the releases from If you need to manually upload releases, you can download the releases from the job artifacts and upload them with twine. You can also make the files locally (not recommended in general, as your local directory is more likely to be "dirty" and SDists love picking up random unrelated/hidden files); this is the procedure:
the job artifacts and upload them with twine. You can also make the files
locally (not recommended in general, as your local directory is more likely
to be "dirty" and SDists love picking up random unrelated/hidden files);
this is the procedure:
.. code-block:: bash .. code-block:: bash
nox -s build python3 -m pip install build
python3 -m build
PYBIND11_SDIST_GLOBAL=1 python3 -m build
twine upload dist/* twine upload dist/*
This makes SDists and wheels, and the final line uploads them. This makes SDists and wheels, and the final line uploads them.

View File

@ -1,6 +1,7 @@
breathe==4.34.0 breathe==4.20.0
furo==2022.6.21 commonmark==0.9.1
sphinx==5.0.2 recommonmark==0.6.0
sphinx-copybutton==0.5.0 sphinx==3.2.1
sphinxcontrib-moderncmakedomain==3.21.4 sphinx_rtd_theme==0.5.0
sphinxcontrib-svg2pdfconverter==1.2.0 sphinxcontrib-moderncmakedomain==3.13
sphinxcontrib-svg2pdfconverter==1.1.0

View File

@ -8,88 +8,6 @@ to a new version. But it goes into more detail. This includes things like
deprecated APIs and their replacements, build system changes, general code deprecated APIs and their replacements, build system changes, general code
modernization and other useful information. modernization and other useful information.
.. _upgrade-guide-2.12:
v2.12
=====
NumPy support has been upgraded to support the 2.x series too. The two relevant
changes are that:
* ``dtype.flags()`` is now a ``uint64`` and ``dtype.alignment()`` an
``ssize_t`` (and NumPy may return an larger than integer value for
``itemsize()`` in NumPy 2.x).
* The long deprecated NumPy function ``PyArray_GetArrayParamsFromObject``
function is not available anymore.
Due to NumPy changes, you may experience difficulties updating to NumPy 2.
Please see the [NumPy 2 migration guide](https://numpy.org/devdocs/numpy_2_0_migration_guide.html) for details.
For example, a more direct change could be that the default integer ``"int_"``
(and ``"uint"``) is now ``ssize_t`` and not ``long`` (affects 64bit windows).
If you want to only support NumPy 1.x for now and are having problems due to
the two internal changes listed above, you can define
``PYBIND11_NUMPY_1_ONLY`` to disable the new support for now. Make sure you
define this on all pybind11 compile units, since it could be a source of ODR
violations if used inconsistently. This option will be removed in the future,
so adapting your code is highly recommended.
.. _upgrade-guide-2.11:
v2.11
=====
* The minimum version of CMake is now 3.5. A future version will likely move to
requiring something like CMake 3.15. Note that CMake 3.27 is removing the
long-deprecated support for ``FindPythonInterp`` if you set 3.27 as the
minimum or maximum supported version. To prepare for that future, CMake 3.15+
using ``FindPython`` or setting ``PYBIND11_FINDPYTHON`` is highly recommended,
otherwise pybind11 will automatically switch to using ``FindPython`` if
``FindPythonInterp`` is not available.
.. _upgrade-guide-2.9:
v2.9
====
* Any usage of the recently added ``py::make_simple_namespace`` should be
converted to using ``py::module_::import("types").attr("SimpleNamespace")``
instead.
* The use of ``_`` in custom type casters can now be replaced with the more
readable ``const_name`` instead. The old ``_`` shortcut has been retained
unless it is being used as a macro (like for gettext).
.. _upgrade-guide-2.7:
v2.7
====
*Before* v2.7, ``py::str`` can hold ``PyUnicodeObject`` or ``PyBytesObject``,
and ``py::isinstance<str>()`` is ``true`` for both ``py::str`` and
``py::bytes``. Starting with v2.7, ``py::str`` exclusively holds
``PyUnicodeObject`` (`#2409 <https://github.com/pybind/pybind11/pull/2409>`_),
and ``py::isinstance<str>()`` is ``true`` only for ``py::str``. To help in
the transition of user code, the ``PYBIND11_STR_LEGACY_PERMISSIVE`` macro
is provided as an escape hatch to go back to the legacy behavior. This macro
will be removed in future releases. Two types of required fixes are expected
to be common:
* Accidental use of ``py::str`` instead of ``py::bytes``, masked by the legacy
behavior. These are probably very easy to fix, by changing from
``py::str`` to ``py::bytes``.
* Reliance on py::isinstance<str>(obj) being ``true`` for
``py::bytes``. This is likely to be easy to fix in most cases by adding
``|| py::isinstance<bytes>(obj)``, but a fix may be more involved, e.g. if
``py::isinstance<T>`` appears in a template. Such situations will require
careful review and custom fixes.
.. _upgrade-guide-2.6: .. _upgrade-guide-2.6:
v2.6 v2.6
@ -274,7 +192,7 @@ way to get and set object state. See :ref:`pickling` for details.
... ...
.def(py::pickle( .def(py::pickle(
[](const Foo &self) { // __getstate__ [](const Foo &self) { // __getstate__
return py::make_tuple(self.value1(), self.value2(), ...); // unchanged return py::make_tuple(f.value1(), f.value2(), ...); // unchanged
}, },
[](py::tuple t) { // __setstate__, note: no `self` argument [](py::tuple t) { // __setstate__, note: no `self` argument
return new Foo(t[0].cast<std::string>(), ...); return new Foo(t[0].cast<std::string>(), ...);
@ -338,7 +256,7 @@ Within pybind11's CMake build system, ``pybind11_add_module`` has always been
setting the ``-fvisibility=hidden`` flag in release mode. From now on, it's setting the ``-fvisibility=hidden`` flag in release mode. From now on, it's
being applied unconditionally, even in debug mode and it can no longer be opted being applied unconditionally, even in debug mode and it can no longer be opted
out of with the ``NO_EXTRAS`` option. The ``pybind11::module`` target now also out of with the ``NO_EXTRAS`` option. The ``pybind11::module`` target now also
adds this flag to its interface. The ``pybind11::embed`` target is unchanged. adds this flag to it's interface. The ``pybind11::embed`` target is unchanged.
The most significant change here is for the ``pybind11::module`` target. If you The most significant change here is for the ``pybind11::module`` target. If you
were previously relying on default visibility, i.e. if your Python module was were previously relying on default visibility, i.e. if your Python module was
@ -566,7 +484,7 @@ include a declaration of the form:
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>) PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
Continuing to do so won't cause an error or even a deprecation warning, Continuing to do so wont cause an error or even a deprecation warning,
but it's completely redundant. but it's completely redundant.

View File

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

View File

@ -19,11 +19,9 @@ PYBIND11_NAMESPACE_BEGIN(detail)
inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) { inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
auto ndim = shape.size(); auto ndim = shape.size();
std::vector<ssize_t> strides(ndim, itemsize); std::vector<ssize_t> strides(ndim, itemsize);
if (ndim > 0) { if (ndim > 0)
for (size_t i = ndim - 1; i > 0; --i) { for (size_t i = ndim - 1; i > 0; --i)
strides[i - 1] = strides[i] * shape[i]; strides[i - 1] = strides[i] * shape[i];
}
}
return strides; return strides;
} }
@ -31,15 +29,11 @@ inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t
inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) { inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
auto ndim = shape.size(); auto ndim = shape.size();
std::vector<ssize_t> strides(ndim, itemsize); std::vector<ssize_t> strides(ndim, itemsize);
for (size_t i = 1; i < ndim; ++i) { for (size_t i = 1; i < ndim; ++i)
strides[i] = strides[i - 1] * shape[i - 1]; strides[i] = strides[i - 1] * shape[i - 1];
}
return strides; return strides;
} }
template <typename T, typename SFINAE = void>
struct compare_buffer_info;
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/// Information record describing a Python buffer object /// Information record describing a Python buffer object
@ -47,89 +41,61 @@ struct buffer_info {
void *ptr = nullptr; // Pointer to the underlying storage void *ptr = nullptr; // Pointer to the underlying storage
ssize_t itemsize = 0; // Size of individual items in bytes ssize_t itemsize = 0; // Size of individual items in bytes
ssize_t size = 0; // Total number of entries ssize_t size = 0; // Total number of entries
std::string format; // For homogeneous buffers, this should be set to std::string format; // For homogeneous buffers, this should be set to format_descriptor<T>::format()
// format_descriptor<T>::format()
ssize_t ndim = 0; // Number of dimensions ssize_t ndim = 0; // Number of dimensions
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension) std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
std::vector<ssize_t> strides; // Number of bytes between adjacent entries std::vector<ssize_t> strides; // Number of bytes between adjacent entries (for each per dimension)
// (for each per dimension)
bool readonly = false; // flag to indicate if the underlying storage may be written to bool readonly = false; // flag to indicate if the underlying storage may be written to
buffer_info() = default; buffer_info() = default;
buffer_info(void *ptr, buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
ssize_t itemsize, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
const std::string &format, : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
ssize_t ndim, shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
detail::any_container<ssize_t> shape_in, if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size())
detail::any_container<ssize_t> strides_in,
bool readonly = false)
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) {
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
} for (size_t i = 0; i < (size_t) ndim; ++i)
for (size_t i = 0; i < (size_t) ndim; ++i) {
size *= shape[i]; size *= shape[i];
}
} }
template <typename T> template <typename T>
buffer_info(T *ptr, buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
detail::any_container<ssize_t> shape_in, : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { }
detail::any_container<ssize_t> strides_in,
bool readonly = false)
: buffer_info(private_ctr_tag(),
ptr,
sizeof(T),
format_descriptor<T>::format(),
static_cast<ssize_t>(shape_in->size()),
std::move(shape_in),
std::move(strides_in),
readonly) {}
buffer_info(void *ptr, buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false)
ssize_t itemsize, : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { }
const std::string &format,
ssize_t size,
bool readonly = false)
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {}
template <typename T> template <typename T>
buffer_info(T *ptr, ssize_t size, bool readonly = false) buffer_info(T *ptr, ssize_t size, bool readonly=false)
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) {} : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) { }
template <typename T> template <typename T>
buffer_info(const T *ptr, ssize_t size, bool readonly = true) buffer_info(const T *ptr, ssize_t size, bool readonly=true)
: buffer_info( : buffer_info(const_cast<T*>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) { }
const_cast<T *>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) {}
explicit buffer_info(Py_buffer *view, bool ownview = true) explicit buffer_info(Py_buffer *view, bool ownview = true)
: buffer_info( : buffer_info(view->buf, view->itemsize, view->format, view->ndim,
view->buf,
view->itemsize,
view->format,
view->ndim,
{view->shape, view->shape + view->ndim}, {view->shape, view->shape + view->ndim},
/* Though buffer::request() requests PyBUF_STRIDES, ctypes objects /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects
* ignore this flag and return a view with NULL strides. * ignore this flag and return a view with NULL strides.
* When strides are NULL, build them manually. */ * When strides are NULL, build them manually. */
view->strides view->strides
? std::vector<ssize_t>(view->strides, view->strides + view->ndim) ? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
(view->readonly != 0)) { view->readonly) {
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
this->m_view = view; this->m_view = view;
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
this->ownview = ownview; this->ownview = ownview;
} }
buffer_info(const buffer_info &) = delete; buffer_info(const buffer_info &) = delete;
buffer_info &operator=(const buffer_info &) = delete; buffer_info& operator=(const buffer_info &) = delete;
buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } buffer_info(buffer_info &&other) {
(*this) = std::move(other);
}
buffer_info &operator=(buffer_info &&rhs) noexcept { buffer_info& operator=(buffer_info &&rhs) {
ptr = rhs.ptr; ptr = rhs.ptr;
itemsize = rhs.itemsize; itemsize = rhs.itemsize;
size = rhs.size; size = rhs.size;
@ -144,39 +110,17 @@ struct buffer_info {
} }
~buffer_info() { ~buffer_info() {
if (m_view && ownview) { if (m_view && ownview) { PyBuffer_Release(m_view); delete m_view; }
PyBuffer_Release(m_view);
delete m_view;
}
} }
Py_buffer *view() const { return m_view; } Py_buffer *view() const { return m_view; }
Py_buffer *&view() { return m_view; } Py_buffer *&view() { return m_view; }
/* True if the buffer item type is equivalent to `T`. */
// To define "equivalent" by example:
// `buffer_info::item_type_is_equivalent_to<int>(b)` and
// `buffer_info::item_type_is_equivalent_to<long>(b)` may both be true
// on some platforms, but `int` and `unsigned` will never be equivalent.
// For the ground truth, please inspect `detail::compare_buffer_info<>`.
template <typename T>
bool item_type_is_equivalent_to() const {
return detail::compare_buffer_info<T>::compare(*this);
}
private: private:
struct private_ctr_tag {}; struct private_ctr_tag { };
buffer_info(private_ctr_tag, buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
void *ptr, detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in, bool readonly)
ssize_t itemsize, : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { }
const std::string &format,
ssize_t ndim,
detail::any_container<ssize_t> &&shape_in,
detail::any_container<ssize_t> &&strides_in,
bool readonly)
: buffer_info(
ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {}
Py_buffer *m_view = nullptr; Py_buffer *m_view = nullptr;
bool ownview = false; bool ownview = false;
@ -184,23 +128,17 @@ private:
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <typename T, typename SFINAE> template <typename T, typename SFINAE = void> struct compare_buffer_info {
struct compare_buffer_info { static bool compare(const buffer_info& b) {
static bool compare(const buffer_info &b) {
// NOLINTNEXTLINE(bugprone-sizeof-expression) Needed for `PyObject *`
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T); return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
} }
}; };
template <typename T> template <typename T> struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> { static bool compare(const buffer_info& b) {
static bool compare(const buffer_info &b) { return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor<T>::value ||
return (size_t) b.itemsize == sizeof(T) ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned<T>::value ? "L" : "l")) ||
&& (b.format == format_descriptor<T>::value ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned<T>::value ? "N" : "n")));
|| ((sizeof(T) == sizeof(long))
&& b.format == (std::is_unsigned<T>::value ? "L" : "l"))
|| ((sizeof(T) == sizeof(size_t))
&& b.format == (std::is_unsigned<T>::value ? "N" : "n")));
} }
}; };

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -15,14 +15,13 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if !defined(PYPY_VERSION) #if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION)
# define PYBIND11_BUILTIN_QUALNAME # define PYBIND11_BUILTIN_QUALNAME
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
#else #else
// In PyPy, we still set __qualname__ so that we can produce reliable function type // In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type
// signatures; in CPython this macro expands to nothing: // signatures; in 3.3+ this macro expands to nothing:
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) \ # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj)
setattr((PyObject *) obj, "__qualname__", nameobj)
#endif #endif
inline std::string get_fully_qualified_tp_name(PyTypeObject *type) { inline std::string get_fully_qualified_tp_name(PyTypeObject *type) {
@ -55,9 +54,6 @@ extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObjec
return PyProperty_Type.tp_descr_set(self, cls, value); return PyProperty_Type.tp_descr_set(self, cls, value);
} }
// Forward declaration to use in `make_static_property_type()`
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type);
/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` /** A `static_property` is the same as a `property` but the `__get__()` and `__set__()`
methods are modified to always use the object type instead of a concrete instance. methods are modified to always use the object type instead of a concrete instance.
Return value: New reference. */ Return value: New reference. */
@ -69,32 +65,24 @@ inline PyTypeObject *make_static_property_type() {
issue no Python C API calls which could potentially invoke the issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */ turn find the newly constructed type in an invalid state) */
auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
if (!heap_type) { if (!heap_type)
pybind11_fail("make_static_property_type(): error allocating type!"); pybind11_fail("make_static_property_type(): error allocating type!");
}
heap_type->ht_name = name_obj.inc_ref().ptr(); heap_type->ht_name = name_obj.inc_ref().ptr();
# ifdef PYBIND11_BUILTIN_QUALNAME #ifdef PYBIND11_BUILTIN_QUALNAME
heap_type->ht_qualname = name_obj.inc_ref().ptr(); heap_type->ht_qualname = name_obj.inc_ref().ptr();
# endif #endif
auto *type = &heap_type->ht_type; auto type = &heap_type->ht_type;
type->tp_name = name; type->tp_name = name;
type->tp_base = type_incref(&PyProperty_Type); type->tp_base = type_incref(&PyProperty_Type);
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
type->tp_descr_get = pybind11_static_get; type->tp_descr_get = pybind11_static_get;
type->tp_descr_set = pybind11_static_set; type->tp_descr_set = pybind11_static_set;
# if PY_VERSION_HEX >= 0x030C0000 if (PyType_Ready(type) < 0)
// Since Python-3.12 property-derived types are required to
// have dynamic attributes (to set `__doc__`)
enable_dynamic_attributes(heap_type);
# endif
if (PyType_Ready(type) < 0) {
pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
}
setattr((PyObject *) type, "__module__", str("pybind11_builtins")); setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
@ -110,17 +98,15 @@ inline PyTypeObject *make_static_property_type() {
inline PyTypeObject *make_static_property_type() { inline PyTypeObject *make_static_property_type() {
auto d = dict(); auto d = dict();
PyObject *result = PyRun_String(R"(\ PyObject *result = PyRun_String(R"(\
class pybind11_static_property(property): class pybind11_static_property(property):
def __get__(self, obj, cls): def __get__(self, obj, cls):
return property.__get__(self, cls, cls) return property.__get__(self, cls, cls)
def __set__(self, obj, value): def __set__(self, obj, value):
cls = obj if isinstance(obj, type) else type(obj) cls = obj if isinstance(obj, type) else type(obj)
property.__set__(self, cls, value) property.__set__(self, cls, value)
)", )", Py_file_input, d.ptr(), d.ptr()
Py_file_input, );
d.ptr(),
d.ptr());
if (result == nullptr) if (result == nullptr)
throw error_already_set(); throw error_already_set();
Py_DECREF(result); Py_DECREF(result);
@ -133,7 +119,7 @@ class pybind11_static_property(property):
By default, Python replaces the `static_property` itself, but for wrapped C++ types By default, Python replaces the `static_property` itself, but for wrapped C++ types
we need to call `static_property.__set__()` in order to propagate the new value to we need to call `static_property.__set__()` in order to propagate the new value to
the underlying C++ data structure. */ the underlying C++ data structure. */
extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyObject *value) { extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) {
// Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
@ -142,10 +128,9 @@ extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyOb
// 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)`
// 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop`
// 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment
auto *const static_prop = (PyObject *) get_internals().static_property_type; const auto static_prop = (PyObject *) get_internals().static_property_type;
const auto call_descr_set = (descr != nullptr) && (value != nullptr) const auto call_descr_set = descr && value && PyObject_IsInstance(descr, static_prop)
&& (PyObject_IsInstance(descr, static_prop) != 0) && !PyObject_IsInstance(value, static_prop);
&& (PyObject_IsInstance(value, static_prop) == 0);
if (call_descr_set) { if (call_descr_set) {
// Call `static_property.__set__()` instead of replacing the `static_property`. // Call `static_property.__set__()` instead of replacing the `static_property`.
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
@ -164,6 +149,7 @@ extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyOb
} }
} }
#if PY_MAJOR_VERSION >= 3
/** /**
* Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing
* methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function,
@ -176,8 +162,11 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name
Py_INCREF(descr); Py_INCREF(descr);
return descr; return descr;
} }
return PyType_Type.tp_getattro(obj, name); else {
return PyType_Type.tp_getattro(obj, name);
}
} }
#endif
/// metaclass `__call__` function that is used to create all pybind11 objects. /// metaclass `__call__` function that is used to create all pybind11 objects.
extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) { extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) {
@ -188,12 +177,13 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
return nullptr; return nullptr;
} }
// This must be a pybind11 instance
auto instance = reinterpret_cast<detail::instance *>(self);
// Ensure that the base __init__ function(s) were called // Ensure that the base __init__ function(s) were called
values_and_holders vhs(self); for (const auto &vh : values_and_holders(instance)) {
for (const auto &vh : vhs) { if (!vh.holder_constructed()) {
if (!vh.holder_constructed() && !vhs.is_redundant_value_and_holder(vh)) { PyErr_Format(PyExc_TypeError, "%.200s.__init__() must be called when overriding __init__",
PyErr_Format(PyExc_TypeError,
"%.200s.__init__() must be called when overriding __init__",
get_fully_qualified_tp_name(vh.type->type).c_str()); get_fully_qualified_tp_name(vh.type->type).c_str());
Py_DECREF(self); Py_DECREF(self);
return nullptr; return nullptr;
@ -212,28 +202,27 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
// 1) be found in internals.registered_types_py // 1) be found in internals.registered_types_py
// 2) have exactly one associated `detail::type_info` // 2) have exactly one associated `detail::type_info`
auto found_type = internals.registered_types_py.find(type); auto found_type = internals.registered_types_py.find(type);
if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1 if (found_type != internals.registered_types_py.end() &&
&& found_type->second[0]->type == type) { found_type->second.size() == 1 &&
found_type->second[0]->type == type) {
auto *tinfo = found_type->second[0]; auto *tinfo = found_type->second[0];
auto tindex = std::type_index(*tinfo->cpptype); auto tindex = std::type_index(*tinfo->cpptype);
internals.direct_conversions.erase(tindex); internals.direct_conversions.erase(tindex);
if (tinfo->module_local) { if (tinfo->module_local)
get_local_internals().registered_types_cpp.erase(tindex); registered_local_types_cpp().erase(tindex);
} else { else
internals.registered_types_cpp.erase(tindex); internals.registered_types_cpp.erase(tindex);
}
internals.registered_types_py.erase(tinfo->type); internals.registered_types_py.erase(tinfo->type);
// Actually just `std::erase_if`, but that's only available in C++20 // Actually just `std::erase_if`, but that's only available in C++20
auto &cache = internals.inactive_override_cache; auto &cache = internals.inactive_override_cache;
for (auto it = cache.begin(), last = cache.end(); it != last;) { for (auto it = cache.begin(), last = cache.end(); it != last; ) {
if (it->first == (PyObject *) tinfo->type) { if (it->first == (PyObject *) tinfo->type)
it = cache.erase(it); it = cache.erase(it);
} else { else
++it; ++it;
}
} }
delete tinfo; delete tinfo;
@ -245,7 +234,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
/** This metaclass is assigned by default to all pybind11 types and is required in order /** This metaclass is assigned by default to all pybind11 types and is required in order
for static properties to function correctly. Users may override this using `py::metaclass`. for static properties to function correctly. Users may override this using `py::metaclass`.
Return value: New reference. */ Return value: New reference. */
inline PyTypeObject *make_default_metaclass() { inline PyTypeObject* make_default_metaclass() {
constexpr auto *name = "pybind11_type"; constexpr auto *name = "pybind11_type";
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name)); auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
@ -253,17 +242,16 @@ inline PyTypeObject *make_default_metaclass() {
issue no Python C API calls which could potentially invoke the issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */ turn find the newly constructed type in an invalid state) */
auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
if (!heap_type) { if (!heap_type)
pybind11_fail("make_default_metaclass(): error allocating metaclass!"); pybind11_fail("make_default_metaclass(): error allocating metaclass!");
}
heap_type->ht_name = name_obj.inc_ref().ptr(); heap_type->ht_name = name_obj.inc_ref().ptr();
#ifdef PYBIND11_BUILTIN_QUALNAME #ifdef PYBIND11_BUILTIN_QUALNAME
heap_type->ht_qualname = name_obj.inc_ref().ptr(); heap_type->ht_qualname = name_obj.inc_ref().ptr();
#endif #endif
auto *type = &heap_type->ht_type; auto type = &heap_type->ht_type;
type->tp_name = name; type->tp_name = name;
type->tp_base = type_incref(&PyType_Type); type->tp_base = type_incref(&PyType_Type);
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
@ -271,13 +259,14 @@ inline PyTypeObject *make_default_metaclass() {
type->tp_call = pybind11_meta_call; type->tp_call = pybind11_meta_call;
type->tp_setattro = pybind11_meta_setattro; type->tp_setattro = pybind11_meta_setattro;
#if PY_MAJOR_VERSION >= 3
type->tp_getattro = pybind11_meta_getattro; type->tp_getattro = pybind11_meta_getattro;
#endif
type->tp_dealloc = pybind11_meta_dealloc; type->tp_dealloc = pybind11_meta_dealloc;
if (PyType_Ready(type) < 0) { if (PyType_Ready(type) < 0)
pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!");
}
setattr((PyObject *) type, "__module__", str("pybind11_builtins")); setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
@ -287,20 +276,16 @@ inline PyTypeObject *make_default_metaclass() {
/// For multiple inheritance types we need to recursively register/deregister base pointers for any /// For multiple inheritance types we need to recursively register/deregister base pointers for any
/// base classes with pointers that are difference from the instance value pointer so that we can /// base classes with pointers that are difference from the instance value pointer so that we can
/// correctly recognize an offset base class pointer. This calls a function with any offset base /// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs.
/// ptrs. inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self,
inline void traverse_offset_bases(void *valueptr, bool (*f)(void * /*parentptr*/, instance * /*self*/)) {
const detail::type_info *tinfo,
instance *self,
bool (*f)(void * /*parentptr*/, instance * /*self*/)) {
for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) { for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) {
if (auto *parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) {
for (auto &c : parent_tinfo->implicit_casts) { for (auto &c : parent_tinfo->implicit_casts) {
if (c.first == tinfo->cpptype) { if (c.first == tinfo->cpptype) {
auto *parentptr = c.second(valueptr); auto *parentptr = c.second(valueptr);
if (parentptr != valueptr) { if (parentptr != valueptr)
f(parentptr, self); f(parentptr, self);
}
traverse_offset_bases(parentptr, parent_tinfo, self, f); traverse_offset_bases(parentptr, parent_tinfo, self, f);
break; break;
} }
@ -327,36 +312,36 @@ inline bool deregister_instance_impl(void *ptr, instance *self) {
inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { inline void register_instance(instance *self, void *valptr, const type_info *tinfo) {
register_instance_impl(valptr, self); register_instance_impl(valptr, self);
if (!tinfo->simple_ancestors) { if (!tinfo->simple_ancestors)
traverse_offset_bases(valptr, tinfo, self, register_instance_impl); traverse_offset_bases(valptr, tinfo, self, register_instance_impl);
}
} }
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) {
bool ret = deregister_instance_impl(valptr, self); bool ret = deregister_instance_impl(valptr, self);
if (!tinfo->simple_ancestors) { if (!tinfo->simple_ancestors)
traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl);
}
return ret; return ret;
} }
/// Instance creation function for all pybind11 types. It allocates the internal instance layout /// Instance creation function for all pybind11 types. It allocates the internal instance layout for
/// for holding C++ objects and holders. Allocation is done lazily (the first time the instance is /// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast
/// cast to a reference or pointer), and initialization is done by an `__init__` function. /// to a reference or pointer), and initialization is done by an `__init__` function.
inline PyObject *make_new_instance(PyTypeObject *type) { inline PyObject *make_new_instance(PyTypeObject *type) {
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
// inherited object is a plain Python type (i.e. not derived from an extension type). Fix it. // object is a a plain Python type (i.e. not derived from an extension type). Fix it.
ssize_t instance_size = static_cast<ssize_t>(sizeof(instance)); ssize_t instance_size = static_cast<ssize_t>(sizeof(instance));
if (type->tp_basicsize < instance_size) { if (type->tp_basicsize < instance_size) {
type->tp_basicsize = instance_size; type->tp_basicsize = instance_size;
} }
#endif #endif
PyObject *self = type->tp_alloc(type, 0); PyObject *self = type->tp_alloc(type, 0);
auto *inst = reinterpret_cast<instance *>(self); auto inst = reinterpret_cast<instance *>(self);
// Allocate the value/holder internals: // Allocate the value/holder internals:
inst->allocate_layout(); inst->allocate_layout();
inst->owned = true;
return self; return self;
} }
@ -372,20 +357,20 @@ extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *,
extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) {
PyTypeObject *type = Py_TYPE(self); PyTypeObject *type = Py_TYPE(self);
std::string msg = get_fully_qualified_tp_name(type) + ": No constructor defined!"; std::string msg = get_fully_qualified_tp_name(type) + ": No constructor defined!";
set_error(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return -1; return -1;
} }
inline void add_patient(PyObject *nurse, PyObject *patient) { inline void add_patient(PyObject *nurse, PyObject *patient) {
auto &internals = get_internals(); auto &internals = get_internals();
auto *instance = reinterpret_cast<detail::instance *>(nurse); auto instance = reinterpret_cast<detail::instance *>(nurse);
instance->has_patients = true; instance->has_patients = true;
Py_INCREF(patient); Py_INCREF(patient);
internals.patients[nurse].push_back(patient); internals.patients[nurse].push_back(patient);
} }
inline void clear_patients(PyObject *self) { inline void clear_patients(PyObject *self) {
auto *instance = reinterpret_cast<detail::instance *>(self); auto instance = reinterpret_cast<detail::instance *>(self);
auto &internals = get_internals(); auto &internals = get_internals();
auto pos = internals.patients.find(self); auto pos = internals.patients.find(self);
assert(pos != internals.patients.end()); assert(pos != internals.patients.end());
@ -395,15 +380,14 @@ inline void clear_patients(PyObject *self) {
auto patients = std::move(pos->second); auto patients = std::move(pos->second);
internals.patients.erase(pos); internals.patients.erase(pos);
instance->has_patients = false; instance->has_patients = false;
for (PyObject *&patient : patients) { for (PyObject *&patient : patients)
Py_CLEAR(patient); Py_CLEAR(patient);
}
} }
/// Clears all internal data from the instance and removes it from registered instances in /// Clears all internal data from the instance and removes it from registered instances in
/// preparation for deallocation. /// preparation for deallocation.
inline void clear_instance(PyObject *self) { inline void clear_instance(PyObject *self) {
auto *instance = reinterpret_cast<detail::instance *>(self); auto instance = reinterpret_cast<detail::instance *>(self);
// Deallocate any values/holders, if present: // Deallocate any values/holders, if present:
for (auto &v_h : values_and_holders(instance)) { for (auto &v_h : values_and_holders(instance)) {
@ -411,48 +395,33 @@ inline void clear_instance(PyObject *self) {
// We have to deregister before we call dealloc because, for virtual MI types, we still // We have to deregister before we call dealloc because, for virtual MI types, we still
// need to be able to get the parent pointers. // need to be able to get the parent pointers.
if (v_h.instance_registered() if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type))
&& !deregister_instance(instance, v_h.value_ptr(), v_h.type)) { pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
pybind11_fail(
"pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
}
if (instance->owned || v_h.holder_constructed()) { if (instance->owned || v_h.holder_constructed())
v_h.type->dealloc(v_h); v_h.type->dealloc(v_h);
}
} }
} }
// Deallocate the value/holder layout internals: // Deallocate the value/holder layout internals:
instance->deallocate_layout(); instance->deallocate_layout();
if (instance->weakrefs) { if (instance->weakrefs)
PyObject_ClearWeakRefs(self); PyObject_ClearWeakRefs(self);
}
PyObject **dict_ptr = _PyObject_GetDictPtr(self); PyObject **dict_ptr = _PyObject_GetDictPtr(self);
if (dict_ptr) { if (dict_ptr)
Py_CLEAR(*dict_ptr); Py_CLEAR(*dict_ptr);
}
if (instance->has_patients) { if (instance->has_patients)
clear_patients(self); clear_patients(self);
}
} }
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` /// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
/// to destroy the C++ object itself, while the rest is Python bookkeeping. /// to destroy the C++ object itself, while the rest is Python bookkeeping.
extern "C" inline void pybind11_object_dealloc(PyObject *self) { extern "C" inline void pybind11_object_dealloc(PyObject *self) {
auto *type = Py_TYPE(self);
// If this is a GC tracked object, untrack it first
// Note that the track call is implicitly done by the
// default tp_alloc, which we never override.
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) != 0) {
PyObject_GC_UnTrack(self);
}
clear_instance(self); clear_instance(self);
auto type = Py_TYPE(self);
type->tp_free(self); type->tp_free(self);
#if PY_VERSION_HEX < 0x03080000 #if PY_VERSION_HEX < 0x03080000
@ -470,8 +439,6 @@ extern "C" inline void pybind11_object_dealloc(PyObject *self) {
#endif #endif
} }
std::string error_string();
/** Create the type which can be used as a common base for all classes. This is /** Create the type which can be used as a common base for all classes. This is
needed in order to satisfy Python's requirements for multiple inheritance. needed in order to satisfy Python's requirements for multiple inheritance.
Return value: New reference. */ Return value: New reference. */
@ -483,17 +450,16 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
issue no Python C API calls which could potentially invoke the issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */ turn find the newly constructed type in an invalid state) */
auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
if (!heap_type) { if (!heap_type)
pybind11_fail("make_object_base_type(): error allocating type!"); pybind11_fail("make_object_base_type(): error allocating type!");
}
heap_type->ht_name = name_obj.inc_ref().ptr(); heap_type->ht_name = name_obj.inc_ref().ptr();
#ifdef PYBIND11_BUILTIN_QUALNAME #ifdef PYBIND11_BUILTIN_QUALNAME
heap_type->ht_qualname = name_obj.inc_ref().ptr(); heap_type->ht_qualname = name_obj.inc_ref().ptr();
#endif #endif
auto *type = &heap_type->ht_type; auto type = &heap_type->ht_type;
type->tp_name = name; type->tp_name = name;
type->tp_base = type_incref(&PyBaseObject_Type); type->tp_base = type_incref(&PyBaseObject_Type);
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance)); type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
@ -506,9 +472,8 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
/* Support weak references (needed for the keep_alive feature) */ /* Support weak references (needed for the keep_alive feature) */
type->tp_weaklistoffset = offsetof(instance, weakrefs); type->tp_weaklistoffset = offsetof(instance, weakrefs);
if (PyType_Ready(type) < 0) { if (PyType_Ready(type) < 0)
pybind11_fail("PyType_Ready failed in make_object_base_type(): " + error_string()); pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
}
setattr((PyObject *) type, "__module__", str("pybind11_builtins")); setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
@ -517,56 +482,56 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
return (PyObject *) heap_type; return (PyObject *) heap_type;
} }
/// dynamic_attr: Support for `d = instance.__dict__`.
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
PyObject *&dict = *_PyObject_GetDictPtr(self);
if (!dict)
dict = PyDict_New();
Py_XINCREF(dict);
return dict;
}
/// dynamic_attr: Support for `instance.__dict__ = dict()`.
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
if (!PyDict_Check(new_dict)) {
PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'",
get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str());
return -1;
}
PyObject *&dict = *_PyObject_GetDictPtr(self);
Py_INCREF(new_dict);
Py_CLEAR(dict);
dict = new_dict;
return 0;
}
/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. /// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
#if PY_VERSION_HEX >= 0x030D0000
PyObject_VisitManagedDict(self, visit, arg);
#else
PyObject *&dict = *_PyObject_GetDictPtr(self); PyObject *&dict = *_PyObject_GetDictPtr(self);
Py_VISIT(dict); Py_VISIT(dict);
#endif
// https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse
#if PY_VERSION_HEX >= 0x03090000
Py_VISIT(Py_TYPE(self));
#endif
return 0; return 0;
} }
/// dynamic_attr: Allow the GC to clear the dictionary. /// dynamic_attr: Allow the GC to clear the dictionary.
extern "C" inline int pybind11_clear(PyObject *self) { extern "C" inline int pybind11_clear(PyObject *self) {
#if PY_VERSION_HEX >= 0x030D0000
PyObject_ClearManagedDict(self);
#else
PyObject *&dict = *_PyObject_GetDictPtr(self); PyObject *&dict = *_PyObject_GetDictPtr(self);
Py_CLEAR(dict); Py_CLEAR(dict);
#endif
return 0; return 0;
} }
/// Give instances of this type a `__dict__` and opt into garbage collection. /// Give instances of this type a `__dict__` and opt into garbage collection.
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
auto *type = &heap_type->ht_type; auto type = &heap_type->ht_type;
type->tp_flags |= Py_TPFLAGS_HAVE_GC; type->tp_flags |= Py_TPFLAGS_HAVE_GC;
#if PY_VERSION_HEX < 0x030B0000 type->tp_dictoffset = type->tp_basicsize; // place dict at the end
type->tp_dictoffset = type->tp_basicsize; // place dict at the end type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it
type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
#else
type->tp_flags |= Py_TPFLAGS_MANAGED_DICT;
#endif
type->tp_traverse = pybind11_traverse; type->tp_traverse = pybind11_traverse;
type->tp_clear = pybind11_clear; type->tp_clear = pybind11_clear;
static PyGetSetDef getset[] = {{ static PyGetSetDef getset[] = {
#if PY_VERSION_HEX < 0x03070000 {const_cast<char*>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
const_cast<char *>("__dict__"), {nullptr, nullptr, nullptr, nullptr, nullptr}
#else };
"__dict__",
#endif
PyObject_GenericGetDict,
PyObject_GenericSetDict,
nullptr,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr}};
type->tp_getset = getset; type->tp_getset = getset;
} }
@ -576,42 +541,38 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
type_info *tinfo = nullptr; type_info *tinfo = nullptr;
for (auto type : reinterpret_borrow<tuple>(Py_TYPE(obj)->tp_mro)) { for (auto type : reinterpret_borrow<tuple>(Py_TYPE(obj)->tp_mro)) {
tinfo = get_type_info((PyTypeObject *) type.ptr()); tinfo = get_type_info((PyTypeObject *) type.ptr());
if (tinfo && tinfo->get_buffer) { if (tinfo && tinfo->get_buffer)
break; break;
}
} }
if (view == nullptr || !tinfo || !tinfo->get_buffer) { if (view == nullptr || !tinfo || !tinfo->get_buffer) {
if (view) { if (view)
view->obj = nullptr; view->obj = nullptr;
} PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
set_error(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
return -1; return -1;
} }
std::memset(view, 0, sizeof(Py_buffer)); std::memset(view, 0, sizeof(Py_buffer));
buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
delete info;
// view->obj = nullptr; // Was just memset to 0, so not necessary
set_error(PyExc_BufferError, "Writable buffer requested for readonly storage");
return -1;
}
view->obj = obj; view->obj = obj;
view->ndim = 1; view->ndim = 1;
view->internal = info; view->internal = info;
view->buf = info->ptr; view->buf = info->ptr;
view->itemsize = info->itemsize; view->itemsize = info->itemsize;
view->len = view->itemsize; view->len = view->itemsize;
for (auto s : info->shape) { for (auto s : info->shape)
view->len *= s; view->len *= s;
view->readonly = info->readonly;
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
if (view)
view->obj = nullptr;
PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage");
return -1;
} }
view->readonly = static_cast<int>(info->readonly); if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) {
view->format = const_cast<char *>(info->format.c_str()); view->format = const_cast<char *>(info->format.c_str());
}
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
view->ndim = (int) info->ndim; view->ndim = (int) info->ndim;
view->strides = info->strides.data(); view->strides = &info->strides[0];
view->shape = info->shape.data(); view->shape = &info->shape[0];
} }
Py_INCREF(view->obj); Py_INCREF(view->obj);
return 0; return 0;
@ -625,6 +586,9 @@ extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) {
/// Give this type a buffer interface. /// Give this type a buffer interface.
inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer;
#if PY_MAJOR_VERSION < 3
heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
#endif
heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer;
heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer;
@ -632,68 +596,70 @@ inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
/** Create a brand new Python type according to the `type_record` specification. /** Create a brand new Python type according to the `type_record` specification.
Return value: New reference. */ Return value: New reference. */
inline PyObject *make_new_python_type(const type_record &rec) { inline PyObject* make_new_python_type(const type_record &rec) {
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name)); auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name));
auto qualname = name; auto qualname = name;
if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) {
#if PY_MAJOR_VERSION >= 3
qualname = reinterpret_steal<object>( qualname = reinterpret_steal<object>(
PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr()));
#else
qualname = str(rec.scope.attr("__qualname__").cast<std::string>() + "." + rec.name);
#endif
} }
object module_; object module_;
if (rec.scope) { if (rec.scope) {
if (hasattr(rec.scope, "__module__")) { if (hasattr(rec.scope, "__module__"))
module_ = rec.scope.attr("__module__"); module_ = rec.scope.attr("__module__");
} else if (hasattr(rec.scope, "__name__")) { else if (hasattr(rec.scope, "__name__"))
module_ = rec.scope.attr("__name__"); module_ = rec.scope.attr("__name__");
}
} }
const auto *full_name = c_str( auto full_name = c_str(
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
module_ ? str(module_).cast<std::string>() + "." + rec.name : module_ ? str(module_).cast<std::string>() + "." + rec.name :
#endif #endif
rec.name); rec.name);
char *tp_doc = nullptr; char *tp_doc = nullptr;
if (rec.doc && options::show_user_defined_docstrings()) { if (rec.doc && options::show_user_defined_docstrings()) {
/* Allocate memory for docstring (using PyObject_MALLOC, since /* Allocate memory for docstring (using PyObject_MALLOC, since
Python will free this later on) */ Python will free this later on) */
size_t size = std::strlen(rec.doc) + 1; size_t size = strlen(rec.doc) + 1;
tp_doc = (char *) PyObject_MALLOC(size); tp_doc = (char *) PyObject_MALLOC(size);
std::memcpy((void *) tp_doc, rec.doc, size); memcpy((void *) tp_doc, rec.doc, size);
} }
auto &internals = get_internals(); auto &internals = get_internals();
auto bases = tuple(rec.bases); auto bases = tuple(rec.bases);
auto *base = (bases.empty()) ? internals.instance_base : bases[0].ptr(); auto base = (bases.empty()) ? internals.instance_base
: bases[0].ptr();
/* Danger zone: from now (and until PyType_Ready), make sure to /* Danger zone: from now (and until PyType_Ready), make sure to
issue no Python C API calls which could potentially invoke the issue no Python C API calls which could potentially invoke the
garbage collector (the GC will call type_traverse(), which will in garbage collector (the GC will call type_traverse(), which will in
turn find the newly constructed type in an invalid state) */ turn find the newly constructed type in an invalid state) */
auto *metaclass auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr()
= rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() : internals.default_metaclass; : internals.default_metaclass;
auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
if (!heap_type) { if (!heap_type)
pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); pybind11_fail(std::string(rec.name) + ": Unable to create type object!");
}
heap_type->ht_name = name.release().ptr(); heap_type->ht_name = name.release().ptr();
#ifdef PYBIND11_BUILTIN_QUALNAME #ifdef PYBIND11_BUILTIN_QUALNAME
heap_type->ht_qualname = qualname.inc_ref().ptr(); heap_type->ht_qualname = qualname.inc_ref().ptr();
#endif #endif
auto *type = &heap_type->ht_type; auto type = &heap_type->ht_type;
type->tp_name = full_name; type->tp_name = full_name;
type->tp_doc = tp_doc; type->tp_doc = tp_doc;
type->tp_base = type_incref((PyTypeObject *) base); type->tp_base = type_incref((PyTypeObject *)base);
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance)); type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
if (!bases.empty()) { if (!bases.empty())
type->tp_bases = bases.release().ptr(); type->tp_bases = bases.release().ptr();
}
/* Don't inherit base __init__ */ /* Don't inherit base __init__ */
type->tp_init = pybind11_object_init; type->tp_init = pybind11_object_init;
@ -702,42 +668,38 @@ inline PyObject *make_new_python_type(const type_record &rec) {
type->tp_as_number = &heap_type->as_number; type->tp_as_number = &heap_type->as_number;
type->tp_as_sequence = &heap_type->as_sequence; type->tp_as_sequence = &heap_type->as_sequence;
type->tp_as_mapping = &heap_type->as_mapping; type->tp_as_mapping = &heap_type->as_mapping;
#if PY_VERSION_HEX >= 0x03050000
type->tp_as_async = &heap_type->as_async; type->tp_as_async = &heap_type->as_async;
#endif
/* Flags */ /* Flags */
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE;
if (!rec.is_final) { #if PY_MAJOR_VERSION < 3
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
#endif
if (!rec.is_final)
type->tp_flags |= Py_TPFLAGS_BASETYPE; type->tp_flags |= Py_TPFLAGS_BASETYPE;
}
if (rec.dynamic_attr) { if (rec.dynamic_attr)
enable_dynamic_attributes(heap_type); enable_dynamic_attributes(heap_type);
}
if (rec.buffer_protocol) { if (rec.buffer_protocol)
enable_buffer_protocol(heap_type); enable_buffer_protocol(heap_type);
}
if (rec.custom_type_setup_callback) { if (PyType_Ready(type) < 0)
rec.custom_type_setup_callback(heap_type); pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");
}
if (PyType_Ready(type) < 0) { assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed: " + error_string()); : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
}
assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
/* Register type with the parent scope */ /* Register type with the parent scope */
if (rec.scope) { if (rec.scope)
setattr(rec.scope, rec.name, (PyObject *) type); setattr(rec.scope, rec.name, (PyObject *) type);
} else { else
Py_INCREF(type); // Keep it alive forever (reference leak) Py_INCREF(type); // Keep it alive forever (reference leak)
}
if (module_) { // Needed by pydoc if (module_) // Needed by pydoc
setattr((PyObject *) type, "__module__", module_); setattr((PyObject *) type, "__module__", module_);
}
PYBIND11_SET_OLDPY_QUALNAME(type, qualname); PYBIND11_SET_OLDPY_QUALNAME(type, qualname);

File diff suppressed because it is too large Load Diff

View File

@ -1,77 +0,0 @@
// Copyright (c) 2024 The pybind Community.
#pragma once
#include <pybind11/pytypes.h>
#include "common.h"
#include "internals.h"
#include <typeinfo>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
// Forward declaration needed here: Refactoring opportunity.
extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *);
inline bool type_is_managed_by_our_internals(PyTypeObject *type_obj) {
#if defined(PYPY_VERSION)
auto &internals = get_internals();
return bool(internals.registered_types_py.find(type_obj)
!= internals.registered_types_py.end());
#else
return bool(type_obj->tp_new == pybind11_object_new);
#endif
}
inline bool is_instance_method_of_type(PyTypeObject *type_obj, PyObject *attr_name) {
PyObject *descr = _PyType_Lookup(type_obj, attr_name);
return bool((descr != nullptr) && PyInstanceMethod_Check(descr));
}
inline object try_get_cpp_conduit_method(PyObject *obj) {
if (PyType_Check(obj)) {
return object();
}
PyTypeObject *type_obj = Py_TYPE(obj);
str attr_name("_pybind11_conduit_v1_");
bool assumed_to_be_callable = false;
if (type_is_managed_by_our_internals(type_obj)) {
if (!is_instance_method_of_type(type_obj, attr_name.ptr())) {
return object();
}
assumed_to_be_callable = true;
}
PyObject *method = PyObject_GetAttr(obj, attr_name.ptr());
if (method == nullptr) {
PyErr_Clear();
return object();
}
if (!assumed_to_be_callable && PyCallable_Check(method) == 0) {
Py_DECREF(method);
return object();
}
return reinterpret_steal<object>(method);
}
inline void *try_raw_pointer_ephemeral_from_cpp_conduit(handle src,
const std::type_info *cpp_type_info) {
object method = try_get_cpp_conduit_method(src.ptr());
if (method) {
capsule cpp_type_info_capsule(const_cast<void *>(static_cast<const void *>(cpp_type_info)),
typeid(std::type_info).name());
object cpp_conduit = method(bytes(PYBIND11_PLATFORM_ABI_ID),
cpp_type_info_capsule,
bytes("raw_pointer_ephemeral"));
if (isinstance<capsule>(cpp_conduit)) {
return reinterpret_borrow<capsule>(cpp_conduit).get_pointer();
}
}
return nullptr;
}
#define PYBIND11_HAS_CPP_CONDUIT 1
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

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

View File

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

View File

@ -9,52 +9,10 @@
#pragma once #pragma once
#include "common.h"
#if defined(WITH_THREAD) && defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# include "../gil.h"
#endif
#include "../pytypes.h" #include "../pytypes.h"
#include <exception>
/// Tracks the `internals` and `type_info` ABI version independent of the main library version.
///
/// Some portions of the code use an ABI that is conditional depending on this
/// version number. That allows ABI-breaking changes to be "pre-implemented".
/// Once the default version number is incremented, the conditional logic that
/// no longer applies can be removed. Additionally, users that need not
/// maintain ABI compatibility can increase the version number in order to take
/// advantage of any functionality/efficiency improvements that depend on the
/// newer ABI.
///
/// WARNING: If you choose to manually increase the ABI version, note that
/// pybind11 may not be tested as thoroughly with a non-default ABI version, and
/// further ABI-incompatible changes may be made before the ABI is officially
/// changed to the new version.
#ifndef PYBIND11_INTERNALS_VERSION
# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
// Version bump for Python 3.12+, before first 3.12 beta release.
// Version bump for MSVC piggy-backed on PR #4779. See comments there.
# define PYBIND11_INTERNALS_VERSION 5
# else
# define PYBIND11_INTERNALS_VERSION 4
# endif
#endif
// This requirement is mainly to reduce the support burden (see PR #4570).
static_assert(PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5,
"pybind11 ABI version 5 is the minimum for Python 3.12+");
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
using ExceptionTranslator = void (*)(std::exception_ptr);
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
constexpr const char *internals_function_record_capsule_name = "pybind11_function_record_capsule";
// Forward declarations // Forward declarations
inline PyTypeObject *make_static_property_type(); inline PyTypeObject *make_static_property_type();
inline PyTypeObject *make_default_metaclass(); inline PyTypeObject *make_default_metaclass();
@ -63,64 +21,30 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new // The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new
// Thread Specific Storage (TSS) API. // Thread Specific Storage (TSS) API.
#if PY_VERSION_HEX >= 0x03070000 #if PY_VERSION_HEX >= 0x03070000
// Avoid unnecessary allocation of `Py_tss_t`, since we cannot use # define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr
// `Py_LIMITED_API` anyway. # define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
# if PYBIND11_INTERNALS_VERSION > 4 # define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
# define PYBIND11_TLS_KEY_REF Py_tss_t & # define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
# if defined(__clang__) # define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
# define PYBIND11_TLS_KEY_INIT(var) \
_Pragma("clang diagnostic push") /**/ \
_Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
Py_tss_t var \
= Py_tss_NEEDS_INIT; \
_Pragma("clang diagnostic pop")
# elif defined(__GNUC__) && !defined(__INTEL_COMPILER)
# define PYBIND11_TLS_KEY_INIT(var) \
_Pragma("GCC diagnostic push") /**/ \
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
Py_tss_t var \
= Py_tss_NEEDS_INIT; \
_Pragma("GCC diagnostic pop")
# else
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
# endif
# define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0)
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr)
# define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key))
# else
# define PYBIND11_TLS_KEY_REF Py_tss_t *
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr;
# define PYBIND11_TLS_KEY_CREATE(var) \
(((var) = PyThread_tss_alloc()) != nullptr && (PyThread_tss_create((var)) == 0))
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
# endif
#else #else
// Usually an int but a long on Cygwin64 with Python 3.x // Usually an int but a long on Cygwin64 with Python 3.x
# define PYBIND11_TLS_KEY_REF decltype(PyThread_create_key()) # define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0
# define PYBIND11_TLS_KEY_INIT(var) PYBIND11_TLS_KEY_REF var = 0;
# define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1)
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) # define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key))
# if defined(PYPY_VERSION) # if PY_MAJOR_VERSION < 3
// On CPython < 3.4 and on PyPy, `PyThread_set_key_value` strangely does not set # define PYBIND11_TLS_DELETE_VALUE(key) \
// the value if it has already been set. Instead, it must first be deleted and PyThread_delete_key_value(key)
// then set again. # define PYBIND11_TLS_REPLACE_VALUE(key, value) \
inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) { do { \
PyThread_delete_key_value(key); PyThread_delete_key_value((key)); \
PyThread_set_key_value(key, value); PyThread_set_key_value((key), (value)); \
} } while (false)
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_delete_key_value(key)
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
::pybind11::detail::tls_replace_value((key), (value))
# else # else
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_set_key_value((key), nullptr) # define PYBIND11_TLS_DELETE_VALUE(key) \
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_set_key_value((key), (value)) PyThread_set_key_value((key), nullptr)
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
PyThread_set_key_value((key), (value))
# endif # endif
# define PYBIND11_TLS_FREE(key) (void) key # define PYBIND11_TLS_FREE(key) (void)key
#endif #endif
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly // Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
@ -129,8 +53,7 @@ inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) {
// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, // libstdc++, this doesn't happen: equality and the type_index hash are based on the type name,
// which works. If not under a known-good stl, provide our own name-based hash and equality // which works. If not under a known-good stl, provide our own name-based hash and equality
// functions that use the type name. // functions that use the type name.
#if (PYBIND11_INTERNALS_VERSION <= 4 && defined(__GLIBCXX__)) \ #if defined(__GLIBCXX__)
|| (PYBIND11_INTERNALS_VERSION >= 5 && !defined(_LIBCPP_VERSION))
inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; }
using type_hash = std::hash<std::type_index>; using type_hash = std::hash<std::type_index>;
using type_equal_to = std::equal_to<std::type_index>; using type_equal_to = std::equal_to<std::type_index>;
@ -143,9 +66,8 @@ struct type_hash {
size_t operator()(const std::type_index &t) const { size_t operator()(const std::type_index &t) const {
size_t hash = 5381; size_t hash = 5381;
const char *ptr = t.name(); const char *ptr = t.name();
while (auto c = static_cast<unsigned char>(*ptr++)) { while (auto c = static_cast<unsigned char>(*ptr++))
hash = (hash * 33) ^ c; hash = (hash * 33) ^ c;
}
return hash; return hash;
} }
}; };
@ -161,9 +83,9 @@ template <typename value_type>
using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>; using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>;
struct override_hash { struct override_hash {
inline size_t operator()(const std::pair<const PyObject *, const char *> &v) const { inline size_t operator()(const std::pair<const PyObject *, const char *>& v) const {
size_t value = std::hash<const void *>()(v.first); size_t value = std::hash<const void *>()(v.first);
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value << 6) + (value >> 2); value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2);
return value; return value;
} }
}; };
@ -172,56 +94,30 @@ struct override_hash {
/// Whenever binary incompatible changes are made to this structure, /// Whenever binary incompatible changes are made to this structure,
/// `PYBIND11_INTERNALS_VERSION` must be incremented. /// `PYBIND11_INTERNALS_VERSION` must be incremented.
struct internals { struct internals {
// std::type_index -> pybind11's type information type_map<type_info *> registered_types_cpp; // std::type_index -> pybind11's type information
type_map<type_info *> registered_types_cpp; std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; // PyTypeObject* -> base type_info(s)
// PyTypeObject* -> base type_info(s) std::unordered_multimap<const void *, instance*> registered_instances; // void * -> instance*
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; std::unordered_set<std::pair<const PyObject *, const char *>, override_hash> inactive_override_cache;
std::unordered_multimap<const void *, instance *> registered_instances; // void * -> instance*
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash>
inactive_override_cache;
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions; type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
std::unordered_map<const PyObject *, std::vector<PyObject *>> patients; std::unordered_map<const PyObject *, std::vector<PyObject *>> patients;
std::forward_list<ExceptionTranslator> registered_exception_translators; std::forward_list<void (*) (std::exception_ptr)> registered_exception_translators;
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions
// extensions std::vector<PyObject *> loader_patient_stack; // Used by `loader_life_support`
#if PYBIND11_INTERNALS_VERSION == 4 std::forward_list<std::string> static_strings; // Stores the std::strings backing detail::c_str()
std::vector<PyObject *> unused_loader_patient_stack_remove_at_v5;
#endif
std::forward_list<std::string> static_strings; // Stores the std::strings backing
// detail::c_str()
PyTypeObject *static_property_type; PyTypeObject *static_property_type;
PyTypeObject *default_metaclass; PyTypeObject *default_metaclass;
PyObject *instance_base; PyObject *instance_base;
#if defined(WITH_THREAD) #if defined(WITH_THREAD)
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined: PYBIND11_TLS_KEY_INIT(tstate);
PYBIND11_TLS_KEY_INIT(tstate)
# if PYBIND11_INTERNALS_VERSION > 4
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
# endif // PYBIND11_INTERNALS_VERSION > 4
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
PyInterpreterState *istate = nullptr; PyInterpreterState *istate = nullptr;
# if PYBIND11_INTERNALS_VERSION > 4
// Note that we have to use a std::string to allocate memory to ensure a unique address
// We want unique addresses since we use pointer equality to compare function records
std::string function_record_capsule_name = internals_function_record_capsule_name;
# endif
internals() = default;
internals(const internals &other) = delete;
internals &operator=(const internals &other) = delete;
~internals() { ~internals() {
# if PYBIND11_INTERNALS_VERSION > 4
PYBIND11_TLS_FREE(loader_life_support_tls_key);
# endif // PYBIND11_INTERNALS_VERSION > 4
// This destructor is called *after* Py_Finalize() in finalize_interpreter(). // This destructor is called *after* Py_Finalize() in finalize_interpreter().
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is // That *SHOULD BE* fine. The following details what happens whe PyThread_tss_free is called.
// called. PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does // PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing.
// nothing. PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree. // PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
// PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). // PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither
// Neither of those have anything to do with CPython internals. PyMem_RawFree *requires* // of those have anything to do with CPython internals.
// that the `tstate` be allocated with the CPython allocator. // PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator.
PYBIND11_TLS_FREE(tstate); PYBIND11_TLS_FREE(tstate);
} }
#endif #endif
@ -236,16 +132,14 @@ struct type_info {
void *(*operator_new)(size_t); void *(*operator_new)(size_t);
void (*init_instance)(instance *, const void *); void (*init_instance)(instance *, const void *);
void (*dealloc)(value_and_holder &v_h); void (*dealloc)(value_and_holder &v_h);
std::vector<PyObject *(*) (PyObject *, PyTypeObject *)> implicit_conversions; std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
std::vector<std::pair<const std::type_info *, void *(*) (void *)>> implicit_casts; std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts;
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions; std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;
buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; buffer_info *(*get_buffer)(PyObject *, void *) = nullptr;
void *get_buffer_data = nullptr; void *get_buffer_data = nullptr;
void *(*module_local_load)(PyObject *, const type_info *) = nullptr; void *(*module_local_load)(PyObject *, const type_info *) = nullptr;
/* A simple type never occurs as a (direct or indirect) parent /* A simple type never occurs as a (direct or indirect) parent
* of a class that makes use of multiple inheritance. * of a class that makes use of multiple inheritance */
* A type can be simple even if it has non-simple ancestors as long as it has no descendants.
*/
bool simple_type : 1; bool simple_type : 1;
/* True if there is no multiple inheritance in this type's inheritance tree */ /* True if there is no multiple inheritance in this type's inheritance tree */
bool simple_ancestors : 1; bool simple_ancestors : 1;
@ -255,78 +149,72 @@ struct type_info {
bool module_local : 1; bool module_local : 1;
}; };
/// Tracks the `internals` and `type_info` ABI version independent of the main library version
#define PYBIND11_INTERNALS_VERSION 4
/// On MSVC, debug and release builds are not ABI-compatible! /// On MSVC, debug and release builds are not ABI-compatible!
#if defined(_MSC_VER) && defined(_DEBUG) #if defined(_MSC_VER) && defined(_DEBUG)
# define PYBIND11_BUILD_TYPE "_debug" # define PYBIND11_BUILD_TYPE "_debug"
#else #else
# define PYBIND11_BUILD_TYPE "" # define PYBIND11_BUILD_TYPE ""
#endif #endif
/// Let's assume that different compilers are ABI-incompatible. /// Let's assume that different compilers are ABI-incompatible.
/// A user can manually set this string if they know their /// A user can manually set this string if they know their
/// compiler is compatible. /// compiler is compatible.
#ifndef PYBIND11_COMPILER_TYPE #ifndef PYBIND11_COMPILER_TYPE
# if defined(_MSC_VER) # if defined(_MSC_VER)
# define PYBIND11_COMPILER_TYPE "_msvc" # define PYBIND11_COMPILER_TYPE "_msvc"
# elif defined(__INTEL_COMPILER) # elif defined(__INTEL_COMPILER)
# define PYBIND11_COMPILER_TYPE "_icc" # define PYBIND11_COMPILER_TYPE "_icc"
# elif defined(__clang__) # elif defined(__clang__)
# define PYBIND11_COMPILER_TYPE "_clang" # define PYBIND11_COMPILER_TYPE "_clang"
# elif defined(__PGI) # elif defined(__PGI)
# define PYBIND11_COMPILER_TYPE "_pgi" # define PYBIND11_COMPILER_TYPE "_pgi"
# elif defined(__MINGW32__) # elif defined(__MINGW32__)
# define PYBIND11_COMPILER_TYPE "_mingw" # define PYBIND11_COMPILER_TYPE "_mingw"
# elif defined(__CYGWIN__) # elif defined(__CYGWIN__)
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" # define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
# elif defined(__GNUC__) # elif defined(__GNUC__)
# define PYBIND11_COMPILER_TYPE "_gcc" # define PYBIND11_COMPILER_TYPE "_gcc"
# else # else
# define PYBIND11_COMPILER_TYPE "_unknown" # define PYBIND11_COMPILER_TYPE "_unknown"
# endif # endif
#endif #endif
/// Also standard libs /// Also standard libs
#ifndef PYBIND11_STDLIB #ifndef PYBIND11_STDLIB
# if defined(_LIBCPP_VERSION) # if defined(_LIBCPP_VERSION)
# define PYBIND11_STDLIB "_libcpp" # define PYBIND11_STDLIB "_libcpp"
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) # elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
# define PYBIND11_STDLIB "_libstdcpp" # define PYBIND11_STDLIB "_libstdcpp"
# else # else
# define PYBIND11_STDLIB "" # define PYBIND11_STDLIB ""
# endif # endif
#endif #endif
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. /// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
/// On MSVC, changes in _MSC_VER may indicate ABI incompatibility (#2898).
#ifndef PYBIND11_BUILD_ABI #ifndef PYBIND11_BUILD_ABI
# if defined(__GXX_ABI_VERSION) # if defined(__GXX_ABI_VERSION)
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) # define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
# elif defined(_MSC_VER) # else
# define PYBIND11_BUILD_ABI "_mscver" PYBIND11_TOSTRING(_MSC_VER) # define PYBIND11_BUILD_ABI ""
# else # endif
# define PYBIND11_BUILD_ABI ""
# endif
#endif #endif
#ifndef PYBIND11_INTERNALS_KIND #ifndef PYBIND11_INTERNALS_KIND
# if defined(WITH_THREAD) # if defined(WITH_THREAD)
# define PYBIND11_INTERNALS_KIND "" # define PYBIND11_INTERNALS_KIND ""
# else # else
# define PYBIND11_INTERNALS_KIND "_without_thread" # define PYBIND11_INTERNALS_KIND "_without_thread"
# endif # endif
#endif #endif
#define PYBIND11_PLATFORM_ABI_ID \ #define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
PYBIND11_BUILD_TYPE
#define PYBIND11_INTERNALS_ID \ #define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \
"__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
PYBIND11_PLATFORM_ABI_ID "__"
#define PYBIND11_MODULE_LOCAL_ID \
"__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
PYBIND11_PLATFORM_ABI_ID "__"
/// Each module locally stores a pointer to the `internals` data. The data /// Each module locally stores a pointer to the `internals` data. The data
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
@ -335,93 +223,21 @@ inline internals **&get_internals_pp() {
return internals_pp; return internals_pp;
} }
// forward decl
inline void translate_exception(std::exception_ptr);
template <class T,
enable_if_t<std::is_same<std::nested_exception, remove_cvref_t<T>>::value, int> = 0>
bool handle_nested_exception(const T &exc, const std::exception_ptr &p) {
std::exception_ptr nested = exc.nested_ptr();
if (nested != nullptr && nested != p) {
translate_exception(nested);
return true;
}
return false;
}
template <class T,
enable_if_t<!std::is_same<std::nested_exception, remove_cvref_t<T>>::value, int> = 0>
bool handle_nested_exception(const T &exc, const std::exception_ptr &p) {
if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(exc))) {
return handle_nested_exception(*nep, p);
}
return false;
}
inline bool raise_err(PyObject *exc_type, const char *msg) {
if (PyErr_Occurred()) {
raise_from(exc_type, msg);
return true;
}
set_error(exc_type, msg);
return false;
}
inline void translate_exception(std::exception_ptr p) { inline void translate_exception(std::exception_ptr p) {
if (!p) {
return;
}
try { try {
std::rethrow_exception(p); if (p) std::rethrow_exception(p);
} catch (error_already_set &e) { } catch (error_already_set &e) { e.restore(); return;
handle_nested_exception(e, p); } catch (const builtin_exception &e) { e.set_error(); return;
e.restore(); } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return;
return; } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
} catch (const builtin_exception &e) { } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
// Could not use template since it's an abstract class. } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(e))) { } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return;
handle_nested_exception(*nep, p); } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
} } catch (const std::overflow_error &e) { PyErr_SetString(PyExc_OverflowError, e.what()); return;
e.set_error(); } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return;
return;
} catch (const std::bad_alloc &e) {
handle_nested_exception(e, p);
raise_err(PyExc_MemoryError, e.what());
return;
} catch (const std::domain_error &e) {
handle_nested_exception(e, p);
raise_err(PyExc_ValueError, e.what());
return;
} catch (const std::invalid_argument &e) {
handle_nested_exception(e, p);
raise_err(PyExc_ValueError, e.what());
return;
} catch (const std::length_error &e) {
handle_nested_exception(e, p);
raise_err(PyExc_ValueError, e.what());
return;
} catch (const std::out_of_range &e) {
handle_nested_exception(e, p);
raise_err(PyExc_IndexError, e.what());
return;
} catch (const std::range_error &e) {
handle_nested_exception(e, p);
raise_err(PyExc_ValueError, e.what());
return;
} catch (const std::overflow_error &e) {
handle_nested_exception(e, p);
raise_err(PyExc_OverflowError, e.what());
return;
} catch (const std::exception &e) {
handle_nested_exception(e, p);
raise_err(PyExc_RuntimeError, e.what());
return;
} catch (const std::nested_exception &e) {
handle_nested_exception(e, p);
raise_err(PyExc_RuntimeError, "Caught an unknown nested exception!");
return;
} catch (...) { } catch (...) {
raise_err(PyExc_RuntimeError, "Caught an unknown exception!"); PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
return; return;
} }
} }
@ -429,117 +245,64 @@ inline void translate_exception(std::exception_ptr p) {
#if !defined(__GLIBCXX__) #if !defined(__GLIBCXX__)
inline void translate_local_exception(std::exception_ptr p) { inline void translate_local_exception(std::exception_ptr p) {
try { try {
if (p) { if (p) std::rethrow_exception(p);
std::rethrow_exception(p); } catch (error_already_set &e) { e.restore(); return;
} } catch (const builtin_exception &e) { e.set_error(); return;
} catch (error_already_set &e) {
e.restore();
return;
} catch (const builtin_exception &e) {
e.set_error();
return;
} }
} }
#endif #endif
inline object get_python_state_dict() {
object state_dict;
#if PYBIND11_INTERNALS_VERSION <= 4 || PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION)
state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
#else
# if PY_VERSION_HEX < 0x03090000
PyInterpreterState *istate = _PyInterpreterState_Get();
# else
PyInterpreterState *istate = PyInterpreterState_Get();
# endif
if (istate) {
state_dict = reinterpret_borrow<object>(PyInterpreterState_GetDict(istate));
}
#endif
if (!state_dict) {
raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
throw error_already_set();
}
return state_dict;
}
inline object get_internals_obj_from_state_dict(handle state_dict) {
return reinterpret_borrow<object>(dict_getitemstring(state_dict.ptr(), PYBIND11_INTERNALS_ID));
}
inline internals **get_internals_pp_from_capsule(handle obj) {
void *raw_ptr = PyCapsule_GetPointer(obj.ptr(), /*name=*/nullptr);
if (raw_ptr == nullptr) {
raise_from(PyExc_SystemError, "pybind11::detail::get_internals_pp_from_capsule() FAILED");
throw error_already_set();
}
return static_cast<internals **>(raw_ptr);
}
/// Return a reference to the current `internals` data /// Return a reference to the current `internals` data
PYBIND11_NOINLINE internals &get_internals() { PYBIND11_NOINLINE inline internals &get_internals() {
auto **&internals_pp = get_internals_pp(); auto **&internals_pp = get_internals_pp();
if (internals_pp && *internals_pp) { if (internals_pp && *internals_pp)
return **internals_pp; return **internals_pp;
}
#if defined(WITH_THREAD)
# if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
gil_scoped_acquire gil;
# else
// Ensure that the GIL is held since we will need to make Python calls. // Ensure that the GIL is held since we will need to make Python calls.
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals. // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
struct gil_scoped_acquire_local { struct gil_scoped_acquire_local {
gil_scoped_acquire_local() : state(PyGILState_Ensure()) {} gil_scoped_acquire_local() : state (PyGILState_Ensure()) {}
gil_scoped_acquire_local(const gil_scoped_acquire_local &) = delete;
gil_scoped_acquire_local &operator=(const gil_scoped_acquire_local &) = delete;
~gil_scoped_acquire_local() { PyGILState_Release(state); } ~gil_scoped_acquire_local() { PyGILState_Release(state); }
const PyGILState_STATE state; const PyGILState_STATE state;
} gil; } gil;
# endif
#endif
error_scope err_scope;
dict state_dict = get_python_state_dict(); constexpr auto *id = PYBIND11_INTERNALS_ID;
if (object internals_obj = get_internals_obj_from_state_dict(state_dict)) { auto builtins = handle(PyEval_GetBuiltins());
internals_pp = get_internals_pp_from_capsule(internals_obj); if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
} internals_pp = static_cast<internals **>(capsule(builtins[id]));
if (internals_pp && *internals_pp) {
// We loaded the internals through `state_dict`, which means that our `error_already_set` // We loaded builtins through python's builtins, which means that our `error_already_set`
// and `builtin_exception` may be different local classes than the ones set up in the // and `builtin_exception` may be different local classes than the ones set up in the
// initial exception translator, below, so add another for our local exception classes. // initial exception translator, below, so add another for our local exception classes.
// //
// libstdc++ doesn't require this (types there are identified only by name) // libstdc++ doesn't require this (types there are identified only by name)
// libc++ with CPython doesn't require this (types are explicitly exported)
// libc++ with PyPy still need it, awaiting further investigation
#if !defined(__GLIBCXX__) #if !defined(__GLIBCXX__)
(*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
#endif #endif
} else { } else {
if (!internals_pp) { if (!internals_pp) internals_pp = new internals*();
internals_pp = new internals *();
}
auto *&internals_ptr = *internals_pp; auto *&internals_ptr = *internals_pp;
internals_ptr = new internals(); internals_ptr = new internals();
#if defined(WITH_THREAD) #if defined(WITH_THREAD)
#if PY_VERSION_HEX < 0x03090000
PyEval_InitThreads();
#endif
PyThreadState *tstate = PyThreadState_Get(); PyThreadState *tstate = PyThreadState_Get();
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition) #if PY_VERSION_HEX >= 0x03070000
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) { internals_ptr->tstate = PyThread_tss_alloc();
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!"); if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate))
} pybind11_fail("get_internals: could not successfully initialize the TSS key!");
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate); PyThread_tss_set(internals_ptr->tstate, tstate);
#else
# if PYBIND11_INTERNALS_VERSION > 4 internals_ptr->tstate = PyThread_create_key();
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition) if (internals_ptr->tstate == -1)
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) { pybind11_fail("get_internals: could not successfully initialize the TLS key!");
pybind11_fail("get_internals: could not successfully initialize the " PyThread_set_key_value(internals_ptr->tstate, tstate);
"loader_life_support TSS key!"); #endif
}
# endif
internals_ptr->istate = tstate->interp; internals_ptr->istate = tstate->interp;
#endif #endif
state_dict[PYBIND11_INTERNALS_ID] = capsule(internals_pp); builtins[id] = capsule(internals_pp);
internals_ptr->registered_exception_translators.push_front(&translate_exception); internals_ptr->registered_exception_translators.push_front(&translate_exception);
internals_ptr->static_property_type = make_static_property_type(); internals_ptr->static_property_type = make_static_property_type();
internals_ptr->default_metaclass = make_default_metaclass(); internals_ptr->default_metaclass = make_default_metaclass();
@ -548,60 +311,10 @@ PYBIND11_NOINLINE internals &get_internals() {
return **internals_pp; return **internals_pp;
} }
// the internals struct (above) is shared between all the modules. local_internals are only /// Works like `internals.registered_types_cpp`, but for module-local registered types:
// for a single module. Any changes made to internals may require an update to inline type_map<type_info *> &registered_local_types_cpp() {
// PYBIND11_INTERNALS_VERSION, breaking backwards compatibility. local_internals is, by design, static type_map<type_info *> locals{};
// restricted to a single module. Whether a module has local internals or not should not return locals;
// impact any other modules, because the only things accessing the local internals is the
// module that contains them.
struct local_internals {
type_map<type_info *> registered_types_cpp;
std::forward_list<ExceptionTranslator> registered_exception_translators;
#if defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
// For ABI compatibility, we can't store the loader_life_support TLS key in
// the `internals` struct directly. Instead, we store it in `shared_data` and
// cache a copy in `local_internals`. If we allocated a separate TLS key for
// each instance of `local_internals`, we could end up allocating hundreds of
// TLS keys if hundreds of different pybind11 modules are loaded (which is a
// plausible number).
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
// Holds the shared TLS key for the loader_life_support stack.
struct shared_loader_life_support_data {
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
shared_loader_life_support_data() {
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
if (!PYBIND11_TLS_KEY_CREATE(loader_life_support_tls_key)) {
pybind11_fail("local_internals: could not successfully initialize the "
"loader_life_support TLS key!");
}
}
// We can't help but leak the TLS key, because Python never unloads extension modules.
};
local_internals() {
auto &internals = get_internals();
// Get or create the `loader_life_support_stack_key`.
auto &ptr = internals.shared_data["_life_support"];
if (!ptr) {
ptr = new shared_loader_life_support_data;
}
loader_life_support_tls_key
= static_cast<shared_loader_life_support_data *>(ptr)->loader_life_support_tls_key;
}
#endif // defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
};
/// Works like `get_internals`, but for things which are locally registered.
inline local_internals &get_local_internals() {
// Current static can be created in the interpreter finalization routine. If the later will be
// destroyed in another static variable destructor, creation of this static there will cause
// static deinitialization fiasco. In order to avoid it we avoid destruction of the
// local_internals static. One can read more about the problem and current solution here:
// https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
static auto *locals = new local_internals();
return *locals;
} }
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its /// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
@ -615,38 +328,19 @@ const char *c_str(Args &&...args) {
return strings.front().c_str(); return strings.front().c_str();
} }
inline const char *get_function_record_capsule_name() {
#if PYBIND11_INTERNALS_VERSION > 4
return get_internals().function_record_capsule_name.c_str();
#else
return nullptr;
#endif
}
// Determine whether or not the following capsule contains a pybind11 function record.
// Note that we use `internals` to make sure that only ABI compatible records are touched.
//
// This check is currently used in two places:
// - An important optimization in functional.h to avoid overhead in C++ -> Python -> C++
// - The sibling feature of cpp_function to allow overloads
inline bool is_function_record_capsule(const capsule &cap) {
// Pointer equality as we rely on internals() to ensure unique pointers
return cap.name() == get_function_record_capsule_name();
}
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/// Returns a named pointer that is shared among all extension modules (using the same /// Returns a named pointer that is shared among all extension modules (using the same
/// pybind11 version) running in the current interpreter. Names starting with underscores /// pybind11 version) running in the current interpreter. Names starting with underscores
/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. /// are reserved for internal usage. Returns `nullptr` if no matching entry was found.
PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) {
auto &internals = detail::get_internals(); auto &internals = detail::get_internals();
auto it = internals.shared_data.find(name); auto it = internals.shared_data.find(name);
return it != internals.shared_data.end() ? it->second : nullptr; return it != internals.shared_data.end() ? it->second : nullptr;
} }
/// Set the shared data that can be later recovered by `get_shared_data()`. /// Set the shared data that can be later recovered by `get_shared_data()`.
PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
detail::get_internals().shared_data[name] = data; detail::get_internals().shared_data[name] = data;
return data; return data;
} }
@ -654,7 +348,7 @@ PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if /// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if
/// such entry exists. Otherwise, a new object of default-constructible type `T` is /// such entry exists. Otherwise, a new object of default-constructible type `T` is
/// added to the shared data under the given name and a reference to it is returned. /// added to the shared data under the given name and a reference to it is returned.
template <typename T> template<typename T>
T &get_or_create_shared_data(const std::string &name) { T &get_or_create_shared_data(const std::string &name) {
auto &internals = detail::get_internals(); auto &internals = detail::get_internals();
auto it = internals.shared_data.find(name); auto it = internals.shared_data.find(name);

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -1,9 +0,0 @@
// Copyright (c) 2023 The pybind Community.
#pragma once
// Common message for `static_assert()`s, which are useful to easily
// preempt much less obvious errors.
#define PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED \
"Pointer types (in particular `PyObject *`) are not supported as scalar types for Eigen " \
"types."

View File

@ -1,714 +0,0 @@
/*
pybind11/eigen/matrix.h: Transparent conversion for dense and sparse Eigen matrices
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
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 "../numpy.h"
#include "common.h"
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
See also:
https://stackoverflow.com/questions/2579576/i-dir-vs-isystem-dir
https://stackoverflow.com/questions/1741816/isystem-for-ms-visual-studio-c-compiler
*/
PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_MSVC(5054) // https://github.com/pybind/pybind11/pull/3741
// C5054: operator '&': deprecated between enumerations of different types
#if defined(__MINGW32__)
PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
#endif
#include <Eigen/Core>
#include <Eigen/SparseCore>
PYBIND11_WARNING_POP
// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit
// move constructors that break things. We could detect this an explicitly copy, but an extra copy
// of matrices seems highly undesirable.
static_assert(EIGEN_VERSION_AT_LEAST(3, 2, 7),
"Eigen matrix support in pybind11 requires Eigen >= 3.2.7");
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_WARNING_DISABLE_MSVC(4127)
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
template <typename MatrixType>
using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
template <typename MatrixType>
using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
PYBIND11_NAMESPACE_BEGIN(detail)
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
using EigenIndex = Eigen::Index;
template <typename Scalar, int Flags, typename StorageIndex>
using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
#else
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
template <typename Scalar, int Flags, typename StorageIndex>
using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
#endif
// Matches Eigen::Map, Eigen::Ref, blocks, etc:
template <typename T>
using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>,
std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
template <typename T>
using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>;
template <typename T>
using is_eigen_dense_plain
= all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>;
template <typename T>
using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>;
// Test for objects inheriting from EigenBase<Derived> that aren't captured by the above. This
// basically covers anything that can be assigned to a dense matrix but that don't have a typical
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
// SelfAdjointView fall into this category.
template <typename T>
using is_eigen_other
= all_of<is_template_base_of<Eigen::EigenBase, T>,
negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>>;
// Captures numpy/eigen conformability status (returned by EigenProps::conformable()):
template <bool EigenRowMajor>
struct EigenConformable {
bool conformable = false;
EigenIndex rows = 0, cols = 0;
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
bool negativestrides = false; // If true, do not use stride!
// NOLINTNEXTLINE(google-explicit-constructor)
EigenConformable(bool fits = false) : conformable{fits} {}
// Matrix type:
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride)
: conformable{true}, rows{r}, cols{c},
// TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
// http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
: (cstride > 0 ? cstride : 0) /* outer stride */,
EigenRowMajor ? (cstride > 0 ? cstride : 0)
: (rstride > 0 ? rstride : 0) /* inner stride */},
negativestrides{rstride < 0 || cstride < 0} {}
// Vector type:
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride)
: EigenConformable(r, c, r == 1 ? c * stride : stride, c == 1 ? r : r * stride) {}
template <typename props>
bool stride_compatible() const {
// To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
// matching strides, or a dimension size of 1 (in which case the stride value is
// irrelevant). Alternatively, if any dimension size is 0, the strides are not relevant
// (and numpy ≥ 1.23 sets the strides to 0 in that case, so we need to check explicitly).
if (negativestrides) {
return false;
}
if (rows == 0 || cols == 0) {
return true;
}
return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
|| (EigenRowMajor ? cols : rows) == 1)
&& (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
|| (EigenRowMajor ? rows : cols) == 1);
}
// NOLINTNEXTLINE(google-explicit-constructor)
operator bool() const { return conformable; }
};
template <typename Type>
struct eigen_extract_stride {
using type = Type;
};
template <typename PlainObjectType, int MapOptions, typename StrideType>
struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> {
using type = StrideType;
};
template <typename PlainObjectType, int Options, typename StrideType>
struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> {
using type = StrideType;
};
// Helper struct for extracting information from an Eigen type
template <typename Type_>
struct EigenProps {
using Type = Type_;
using Scalar = typename Type::Scalar;
using StrideType = typename eigen_extract_stride<Type>::type;
static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
size = Type::SizeAtCompileTime;
static constexpr bool row_major = Type::IsRowMajor,
vector
= Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic,
fixed = size != Eigen::Dynamic, // Fully-fixed size
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
template <EigenIndex i, EigenIndex ifzero>
using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
static constexpr EigenIndex inner_stride
= if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
vector ? size
: row_major ? cols
: rows > ::value;
static constexpr bool dynamic_stride
= inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic;
static constexpr bool requires_row_major
= !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1;
static constexpr bool requires_col_major
= !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1;
// Takes an input array and determines whether we can make it fit into the Eigen type. If
// the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector
// (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type).
static EigenConformable<row_major> conformable(const array &a) {
const auto dims = a.ndim();
if (dims < 1 || dims > 2) {
return false;
}
if (dims == 2) { // Matrix type: require exact match (or dynamic)
EigenIndex np_rows = a.shape(0), np_cols = a.shape(1),
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) {
return false;
}
return {np_rows, np_cols, np_rstride, np_cstride};
}
// Otherwise we're storing an n-vector. Only one of the strides will be used, but
// whichever is used, we want the (single) numpy stride value.
const EigenIndex n = a.shape(0),
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
if (vector) { // Eigen type is a compile-time vector
if (fixed && size != n) {
return false; // Vector size mismatch
}
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
}
if (fixed) {
// The type has a fixed size, but is not a vector: abort
return false;
}
if (fixed_cols) {
// Since this isn't a vector, cols must be != 1. We allow this only if it exactly
// equals the number of elements (rows is Dynamic, and so 1 row is allowed).
if (cols != n) {
return false;
}
return {1, n, stride};
} // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
if (fixed_rows && rows != n) {
return false;
}
return {n, 1, stride};
}
static constexpr bool show_writeable
= is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
static constexpr bool show_order = is_eigen_dense_map<Type>::value;
static constexpr bool show_c_contiguous = show_order && requires_row_major;
static constexpr bool show_f_contiguous
= !show_c_contiguous && show_order && requires_col_major;
static constexpr auto descriptor
= const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
+ const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
+ const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]")
+
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
// be satisfied: writeable=True (for a mutable reference), and, depending on the map's
// stride options, possibly f_contiguous or c_contiguous. We include them in the
// descriptor output to provide some hint as to why a TypeError is occurring (otherwise
// it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and
// an error message that you *gave* a numpy.ndarray of the right type and dimensions.
const_name<show_writeable>(", flags.writeable", "")
+ const_name<show_c_contiguous>(", flags.c_contiguous", "")
+ const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]");
};
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
template <typename props>
handle
eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
constexpr ssize_t elem_size = sizeof(typename props::Scalar);
array a;
if (props::vector) {
a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base);
} else {
a = array({src.rows(), src.cols()},
{elem_size * src.rowStride(), elem_size * src.colStride()},
src.data(),
base);
}
if (!writeable) {
array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
}
return a.release();
}
// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that
// reference the Eigen object's data with `base` as the python-registered base class (if omitted,
// the base will be set to None, and lifetime management is up to the caller). The numpy array is
// non-writeable if the given type is const.
template <typename props, typename Type>
handle eigen_ref_array(Type &src, handle parent = none()) {
// none here is to get past array's should-we-copy detection, which currently always
// copies when there is no base. Setting the base to None should be harmless.
return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value);
}
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
// numpy array that references the encapsulated data with a python-side reference to the capsule to
// tie its destruction to that of any dependent python objects. Const-ness is determined by
// whether or not the Type of the pointer given is const.
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
handle eigen_encapsulate(Type *src) {
capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
return eigen_ref_array<props>(*src, base);
}
// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense
// types.
template <typename Type>
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
using Scalar = typename Type::Scalar;
static_assert(!std::is_pointer<Scalar>::value,
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
using props = EigenProps<Type>;
bool load(handle src, bool convert) {
// If we're in no-convert mode, only load if given an array of the correct type
if (!convert && !isinstance<array_t<Scalar>>(src)) {
return false;
}
// Coerce into an array, but don't do type conversion yet; the copy below handles it.
auto buf = array::ensure(src);
if (!buf) {
return false;
}
auto dims = buf.ndim();
if (dims < 1 || dims > 2) {
return false;
}
auto fits = props::conformable(buf);
if (!fits) {
return false;
}
// Allocate the new type, then build a numpy reference into it
value = Type(fits.rows, fits.cols);
auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
if (dims == 1) {
ref = ref.squeeze();
} else if (ref.ndim() == 1) {
buf = buf.squeeze();
}
int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr());
if (result < 0) { // Copy failed!
PyErr_Clear();
return false;
}
return true;
}
private:
// Cast implementation
template <typename CType>
static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
switch (policy) {
case return_value_policy::take_ownership:
case return_value_policy::automatic:
return eigen_encapsulate<props>(src);
case return_value_policy::move:
return eigen_encapsulate<props>(new CType(std::move(*src)));
case return_value_policy::copy:
return eigen_array_cast<props>(*src);
case return_value_policy::reference:
case return_value_policy::automatic_reference:
return eigen_ref_array<props>(*src);
case return_value_policy::reference_internal:
return eigen_ref_array<props>(*src, parent);
default:
throw cast_error("unhandled return_value_policy: should not happen!");
};
}
public:
// Normal returned non-reference, non-const value:
static handle cast(Type &&src, return_value_policy /* policy */, handle parent) {
return cast_impl(&src, return_value_policy::move, parent);
}
// If you return a non-reference const, we mark the numpy array readonly:
static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) {
return cast_impl(&src, return_value_policy::move, parent);
}
// lvalue reference return; default (automatic) becomes copy
static handle cast(Type &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic
|| policy == return_value_policy::automatic_reference) {
policy = return_value_policy::copy;
}
return cast_impl(&src, policy, parent);
}
// const lvalue reference return; default (automatic) becomes copy
static handle cast(const Type &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic
|| policy == return_value_policy::automatic_reference) {
policy = return_value_policy::copy;
}
return cast(&src, policy, parent);
}
// non-const pointer return
static handle cast(Type *src, return_value_policy policy, handle parent) {
return cast_impl(src, policy, parent);
}
// const pointer return
static handle cast(const Type *src, return_value_policy policy, handle parent) {
return cast_impl(src, policy, parent);
}
static constexpr auto name = props::descriptor;
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type *() { return &value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type &() { return value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type &&() && { return std::move(value); }
template <typename T>
using cast_op_type = movable_cast_op_type<T>;
private:
Type value;
};
// Base class for casting reference/map/block/etc. objects back to python.
template <typename MapType>
struct eigen_map_caster {
static_assert(!std::is_pointer<typename MapType::Scalar>::value,
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
private:
using props = EigenProps<MapType>;
public:
// Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has
// to stay around), but we'll allow it under the assumption that you know what you're doing
// (and have an appropriate keep_alive in place). We return a numpy array pointing directly at
// the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
// Note that this means you need to ensure you don't destroy the object in some other way (e.g.
// with an appropriate keep_alive, or with a reference to a statically allocated matrix).
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
switch (policy) {
case return_value_policy::copy:
return eigen_array_cast<props>(src);
case return_value_policy::reference_internal:
return eigen_array_cast<props>(src, parent, is_eigen_mutable_map<MapType>::value);
case return_value_policy::reference:
case return_value_policy::automatic:
case return_value_policy::automatic_reference:
return eigen_array_cast<props>(src, none(), is_eigen_mutable_map<MapType>::value);
default:
// move, take_ownership don't make any sense for a ref/map:
pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type");
}
}
static constexpr auto name = props::descriptor;
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
// types but not bound arguments). We still provide them (with an explicitly delete) so that
// you end up here if you try anyway.
bool load(handle, bool) = delete;
operator MapType() = delete;
template <typename>
using cast_op_type = MapType;
};
// We can return any map-like object (but can only load Refs, specialized next):
template <typename Type>
struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> : eigen_map_caster<Type> {};
// Loader for Ref<...> arguments. See the documentation for info on how to make this work without
// copying (it requires some extra effort in many cases).
template <typename PlainObjectType, typename StrideType>
struct type_caster<
Eigen::Ref<PlainObjectType, 0, StrideType>,
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
: public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
private:
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
using props = EigenProps<Type>;
using Scalar = typename props::Scalar;
static_assert(!std::is_pointer<Scalar>::value,
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
using Array
= array_t<Scalar,
array::forcecast
| ((props::row_major ? props::inner_stride : props::outer_stride) == 1
? array::c_style
: (props::row_major ? props::outer_stride : props::inner_stride) == 1
? array::f_style
: 0)>;
static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value;
// Delay construction (these have no default constructor)
std::unique_ptr<MapType> map;
std::unique_ptr<Type> ref;
// Our array. When possible, this is just a numpy array pointing to the source data, but
// sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
// incompatible layout, or is an array of a type that needs to be converted). Using a numpy
// temporary (rather than an Eigen temporary) saves an extra copy when we need both type
// conversion and storage order conversion. (Note that we refuse to use this temporary copy
// when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
Array copy_or_ref;
public:
bool load(handle src, bool convert) {
// First check whether what we have is already an array of the right type. If not, we
// can't avoid a copy (because the copy is also going to do type conversion).
bool need_copy = !isinstance<Array>(src);
EigenConformable<props::row_major> fits;
if (!need_copy) {
// We don't need a converting copy, but we also need to check whether the strides are
// compatible with the Ref's stride requirements
auto aref = reinterpret_borrow<Array>(src);
if (aref && (!need_writeable || aref.writeable())) {
fits = props::conformable(aref);
if (!fits) {
return false; // Incompatible dimensions
}
if (!fits.template stride_compatible<props>()) {
need_copy = true;
} else {
copy_or_ref = std::move(aref);
}
} else {
need_copy = true;
}
}
if (need_copy) {
// We need to copy: If we need a mutable reference, or we're not supposed to convert
// (either because we're in the no-convert overload pass, or because we're explicitly
// instructed not to copy (via `py::arg().noconvert()`) we have to fail loading.
if (!convert || need_writeable) {
return false;
}
Array copy = Array::ensure(src);
if (!copy) {
return false;
}
fits = props::conformable(copy);
if (!fits || !fits.template stride_compatible<props>()) {
return false;
}
copy_or_ref = std::move(copy);
loader_life_support::add_patient(copy_or_ref);
}
ref.reset();
map.reset(new MapType(data(copy_or_ref),
fits.rows,
fits.cols,
make_stride(fits.stride.outer(), fits.stride.inner())));
ref.reset(new Type(*map));
return true;
}
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type *() { return ref.get(); }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type &() { return *ref; }
template <typename _T>
using cast_op_type = pybind11::detail::cast_op_type<_T>;
private:
template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0>
Scalar *data(Array &a) {
return a.mutable_data();
}
template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0>
const Scalar *data(Array &a) {
return a.data();
}
// Attempt to figure out a constructor of `Stride` that will work.
// If both strides are fixed, use a default constructor:
template <typename S>
using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
&& std::is_default_constructible<S>::value>;
// Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
// Eigen::Stride, and use it:
template <typename S>
using stride_ctor_dual
= bool_constant<!stride_ctor_default<S>::value
&& std::is_constructible<S, EigenIndex, EigenIndex>::value>;
// Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use
// it (passing whichever stride is dynamic).
template <typename S>
using stride_ctor_outer
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
&& S::OuterStrideAtCompileTime == Eigen::Dynamic
&& S::InnerStrideAtCompileTime != Eigen::Dynamic
&& std::is_constructible<S, EigenIndex>::value>;
template <typename S>
using stride_ctor_inner
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
&& S::InnerStrideAtCompileTime == Eigen::Dynamic
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
&& std::is_constructible<S, EigenIndex>::value>;
template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0>
static S make_stride(EigenIndex, EigenIndex) {
return S();
}
template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0>
static S make_stride(EigenIndex outer, EigenIndex inner) {
return S(outer, inner);
}
template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0>
static S make_stride(EigenIndex outer, EigenIndex) {
return S(outer);
}
template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0>
static S make_stride(EigenIndex, EigenIndex inner) {
return S(inner);
}
};
// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not
// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout).
// load() is not supported, but we can cast them into the python domain by first copying to a
// regular Eigen::Matrix, then casting that.
template <typename Type>
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
static_assert(!std::is_pointer<typename Type::Scalar>::value,
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
protected:
using Matrix
= Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
using props = EigenProps<Matrix>;
public:
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
handle h = eigen_encapsulate<props>(new Matrix(src));
return h;
}
static handle cast(const Type *src, return_value_policy policy, handle parent) {
return cast(*src, policy, parent);
}
static constexpr auto name = props::descriptor;
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
// types but not bound arguments). We still provide them (with an explicitly delete) so that
// you end up here if you try anyway.
bool load(handle, bool) = delete;
operator Type() = delete;
template <typename>
using cast_op_type = Type;
};
template <typename Type>
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
using Scalar = typename Type::Scalar;
static_assert(!std::is_pointer<Scalar>::value,
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
using Index = typename Type::Index;
static constexpr bool rowMajor = Type::IsRowMajor;
bool load(handle src, bool) {
if (!src) {
return false;
}
auto obj = reinterpret_borrow<object>(src);
object sparse_module = module_::import("scipy.sparse");
object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
if (!type::handle_of(obj).is(matrix_type)) {
try {
obj = matrix_type(obj);
} catch (const error_already_set &) {
return false;
}
}
auto values = array_t<Scalar>((object) obj.attr("data"));
auto innerIndices = array_t<StorageIndex>((object) obj.attr("indices"));
auto outerIndices = array_t<StorageIndex>((object) obj.attr("indptr"));
auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
auto nnz = obj.attr("nnz").cast<Index>();
if (!values || !innerIndices || !outerIndices) {
return false;
}
value = EigenMapSparseMatrix<Scalar,
Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
StorageIndex>(shape[0].cast<Index>(),
shape[1].cast<Index>(),
std::move(nnz),
outerIndices.mutable_data(),
innerIndices.mutable_data(),
values.mutable_data());
return true;
}
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
const_cast<Type &>(src).makeCompressed();
object matrix_type
= module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
array data(src.nonZeros(), src.valuePtr());
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
array innerIndices(src.nonZeros(), src.innerIndexPtr());
return matrix_type(pybind11::make_tuple(
std::move(data), std::move(innerIndices), std::move(outerIndices)),
pybind11::make_tuple(src.rows(), src.cols()))
.release();
}
PYBIND11_TYPE_CASTER(Type,
const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
"scipy.sparse.csc_matrix[")
+ npy_format_descriptor<Scalar>::name + const_name("]"));
};
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -1,517 +0,0 @@
/*
pybind11/eigen/tensor.h: Transparent conversion for Eigen tensors
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 "../numpy.h"
#include "common.h"
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
static_assert(__GNUC__ > 5, "Eigen Tensor support in pybind11 requires GCC > 5.0");
#endif
// Disable warnings for Eigen
PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_MSVC(4554)
PYBIND11_WARNING_DISABLE_MSVC(4127)
#if defined(__MINGW32__)
PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
#endif
#include <unsupported/Eigen/CXX11/Tensor>
PYBIND11_WARNING_POP
static_assert(EIGEN_VERSION_AT_LEAST(3, 3, 0),
"Eigen Tensor support in pybind11 requires Eigen >= 3.3.0");
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_WARNING_DISABLE_MSVC(4127)
PYBIND11_NAMESPACE_BEGIN(detail)
inline bool is_tensor_aligned(const void *data) {
return (reinterpret_cast<std::size_t>(data) % EIGEN_DEFAULT_ALIGN_BYTES) == 0;
}
template <typename T>
constexpr int compute_array_flag_from_tensor() {
static_assert((static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor))
|| (static_cast<int>(T::Layout) == static_cast<int>(Eigen::ColMajor)),
"Layout must be row or column major");
return (static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor)) ? array::c_style
: array::f_style;
}
template <typename T>
struct eigen_tensor_helper {};
template <typename Scalar_, int NumIndices_, int Options_, typename IndexType>
struct eigen_tensor_helper<Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>> {
using Type = Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>;
using ValidType = void;
static Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape(const Type &f) {
return f.dimensions();
}
static constexpr bool
is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> & /*shape*/) {
return true;
}
template <typename T>
struct helper {};
template <size_t... Is>
struct helper<index_sequence<Is...>> {
static constexpr auto value = ::pybind11::detail::concat(const_name(((void) Is, "?"))...);
};
static constexpr auto dimensions_descriptor
= helper<decltype(make_index_sequence<Type::NumIndices>())>::value;
template <typename... Args>
static Type *alloc(Args &&...args) {
return new Type(std::forward<Args>(args)...);
}
static void free(Type *tensor) { delete tensor; }
};
template <typename Scalar_, typename std::ptrdiff_t... Indices, int Options_, typename IndexType>
struct eigen_tensor_helper<
Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>> {
using Type = Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>;
using ValidType = void;
static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices>
get_shape(const Type & /*f*/) {
return get_shape();
}
static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape() {
return Eigen::DSizes<typename Type::Index, Type::NumIndices>(Indices...);
}
static bool
is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> &shape) {
return get_shape() == shape;
}
static constexpr auto dimensions_descriptor
= ::pybind11::detail::concat(const_name<Indices>()...);
template <typename... Args>
static Type *alloc(Args &&...args) {
Eigen::aligned_allocator<Type> allocator;
return ::new (allocator.allocate(1)) Type(std::forward<Args>(args)...);
}
static void free(Type *tensor) {
Eigen::aligned_allocator<Type> allocator;
tensor->~Type();
allocator.deallocate(tensor, 1);
}
};
template <typename Type, bool ShowDetails, bool NeedsWriteable = false>
struct get_tensor_descriptor {
static constexpr auto details
= const_name<NeedsWriteable>(", flags.writeable", "")
+ const_name<static_cast<int>(Type::Layout) == static_cast<int>(Eigen::RowMajor)>(
", flags.c_contiguous", ", flags.f_contiguous");
static constexpr auto value
= const_name("numpy.ndarray[") + npy_format_descriptor<typename Type::Scalar>::name
+ const_name("[") + eigen_tensor_helper<remove_cv_t<Type>>::dimensions_descriptor
+ const_name("]") + const_name<ShowDetails>(details, const_name("")) + const_name("]");
};
// When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes<T, 0> does not have the begin() member
// function. Falling back to a simple loop works around this issue.
//
// We need to disable the type-limits warning for the inner loop when size = 0.
PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_GCC("-Wtype-limits")
template <typename T, int size>
std::vector<T> convert_dsizes_to_vector(const Eigen::DSizes<T, size> &arr) {
std::vector<T> result(size);
for (size_t i = 0; i < size; i++) {
result[i] = arr[i];
}
return result;
}
template <typename T, int size>
Eigen::DSizes<T, size> get_shape_for_array(const array &arr) {
Eigen::DSizes<T, size> result;
const T *shape = arr.shape();
for (size_t i = 0; i < size; i++) {
result[i] = shape[i];
}
return result;
}
PYBIND11_WARNING_POP
template <typename Type>
struct type_caster<Type, typename eigen_tensor_helper<Type>::ValidType> {
static_assert(!std::is_pointer<typename Type::Scalar>::value,
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
using Helper = eigen_tensor_helper<Type>;
static constexpr auto temp_name = get_tensor_descriptor<Type, false>::value;
PYBIND11_TYPE_CASTER(Type, temp_name);
bool load(handle src, bool convert) {
if (!convert) {
if (!isinstance<array>(src)) {
return false;
}
array temp = array::ensure(src);
if (!temp) {
return false;
}
if (!temp.dtype().is(dtype::of<typename Type::Scalar>())) {
return false;
}
}
array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()> arr(
reinterpret_borrow<object>(src));
if (arr.ndim() != Type::NumIndices) {
return false;
}
auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
if (!Helper::is_correct_shape(shape)) {
return false;
}
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
auto data_pointer = arr.data();
#else
// Handle Eigen bug
auto data_pointer = const_cast<typename Type::Scalar *>(arr.data());
#endif
if (is_tensor_aligned(arr.data())) {
value = Eigen::TensorMap<const Type, Eigen::Aligned>(data_pointer, shape);
} else {
value = Eigen::TensorMap<const Type>(data_pointer, shape);
}
return true;
}
static handle cast(Type &&src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::reference
|| policy == return_value_policy::reference_internal) {
pybind11_fail("Cannot use a reference return value policy for an rvalue");
}
return cast_impl(&src, return_value_policy::move, parent);
}
static handle cast(const Type &&src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::reference
|| policy == return_value_policy::reference_internal) {
pybind11_fail("Cannot use a reference return value policy for an rvalue");
}
return cast_impl(&src, return_value_policy::move, parent);
}
static handle cast(Type &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic
|| policy == return_value_policy::automatic_reference) {
policy = return_value_policy::copy;
}
return cast_impl(&src, policy, parent);
}
static handle cast(const Type &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic
|| policy == return_value_policy::automatic_reference) {
policy = return_value_policy::copy;
}
return cast(&src, policy, parent);
}
static handle cast(Type *src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic) {
policy = return_value_policy::take_ownership;
} else if (policy == return_value_policy::automatic_reference) {
policy = return_value_policy::reference;
}
return cast_impl(src, policy, parent);
}
static handle cast(const Type *src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic) {
policy = return_value_policy::take_ownership;
} else if (policy == return_value_policy::automatic_reference) {
policy = return_value_policy::reference;
}
return cast_impl(src, policy, parent);
}
template <typename C>
static handle cast_impl(C *src, return_value_policy policy, handle parent) {
object parent_object;
bool writeable = false;
switch (policy) {
case return_value_policy::move:
if (std::is_const<C>::value) {
pybind11_fail("Cannot move from a constant reference");
}
src = Helper::alloc(std::move(*src));
parent_object
= capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
writeable = true;
break;
case return_value_policy::take_ownership:
if (std::is_const<C>::value) {
// This cast is ugly, and might be UB in some cases, but we don't have an
// alternative here as we must free that memory
Helper::free(const_cast<Type *>(src));
pybind11_fail("Cannot take ownership of a const reference");
}
parent_object
= capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
writeable = true;
break;
case return_value_policy::copy:
writeable = true;
break;
case return_value_policy::reference:
parent_object = none();
writeable = !std::is_const<C>::value;
break;
case return_value_policy::reference_internal:
// Default should do the right thing
if (!parent) {
pybind11_fail("Cannot use reference internal when there is no parent");
}
parent_object = reinterpret_borrow<object>(parent);
writeable = !std::is_const<C>::value;
break;
default:
pybind11_fail("pybind11 bug in eigen.h, please file a bug report");
}
auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
convert_dsizes_to_vector(Helper::get_shape(*src)), src->data(), parent_object);
if (!writeable) {
array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
}
return result.release();
}
};
template <typename StoragePointerType,
bool needs_writeable,
enable_if_t<!needs_writeable, bool> = true>
StoragePointerType get_array_data_for_type(array &arr) {
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
return reinterpret_cast<StoragePointerType>(arr.data());
#else
// Handle Eigen bug
return reinterpret_cast<StoragePointerType>(const_cast<void *>(arr.data()));
#endif
}
template <typename StoragePointerType,
bool needs_writeable,
enable_if_t<needs_writeable, bool> = true>
StoragePointerType get_array_data_for_type(array &arr) {
return reinterpret_cast<StoragePointerType>(arr.mutable_data());
}
template <typename T, typename = void>
struct get_storage_pointer_type;
template <typename MapType>
struct get_storage_pointer_type<MapType, void_t<typename MapType::StoragePointerType>> {
using SPT = typename MapType::StoragePointerType;
};
template <typename MapType>
struct get_storage_pointer_type<MapType, void_t<typename MapType::PointerArgType>> {
using SPT = typename MapType::PointerArgType;
};
template <typename Type, int Options>
struct type_caster<Eigen::TensorMap<Type, Options>,
typename eigen_tensor_helper<remove_cv_t<Type>>::ValidType> {
static_assert(!std::is_pointer<typename Type::Scalar>::value,
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
using MapType = Eigen::TensorMap<Type, Options>;
using Helper = eigen_tensor_helper<remove_cv_t<Type>>;
bool load(handle src, bool /*convert*/) {
// Note that we have a lot more checks here as we want to make sure to avoid copies
if (!isinstance<array>(src)) {
return false;
}
auto arr = reinterpret_borrow<array>(src);
if ((arr.flags() & compute_array_flag_from_tensor<Type>()) == 0) {
return false;
}
if (!arr.dtype().is(dtype::of<typename Type::Scalar>())) {
return false;
}
if (arr.ndim() != Type::NumIndices) {
return false;
}
constexpr bool is_aligned = (Options & Eigen::Aligned) != 0;
if (is_aligned && !is_tensor_aligned(arr.data())) {
return false;
}
auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
if (!Helper::is_correct_shape(shape)) {
return false;
}
if (needs_writeable && !arr.writeable()) {
return false;
}
auto result = get_array_data_for_type<typename get_storage_pointer_type<MapType>::SPT,
needs_writeable>(arr);
value.reset(new MapType(std::move(result), std::move(shape)));
return true;
}
static handle cast(MapType &&src, return_value_policy policy, handle parent) {
return cast_impl(&src, policy, parent);
}
static handle cast(const MapType &&src, return_value_policy policy, handle parent) {
return cast_impl(&src, policy, parent);
}
static handle cast(MapType &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic
|| policy == return_value_policy::automatic_reference) {
policy = return_value_policy::copy;
}
return cast_impl(&src, policy, parent);
}
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic
|| policy == return_value_policy::automatic_reference) {
policy = return_value_policy::copy;
}
return cast(&src, policy, parent);
}
static handle cast(MapType *src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic) {
policy = return_value_policy::take_ownership;
} else if (policy == return_value_policy::automatic_reference) {
policy = return_value_policy::reference;
}
return cast_impl(src, policy, parent);
}
static handle cast(const MapType *src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic) {
policy = return_value_policy::take_ownership;
} else if (policy == return_value_policy::automatic_reference) {
policy = return_value_policy::reference;
}
return cast_impl(src, policy, parent);
}
template <typename C>
static handle cast_impl(C *src, return_value_policy policy, handle parent) {
object parent_object;
constexpr bool writeable = !std::is_const<C>::value;
switch (policy) {
case return_value_policy::reference:
parent_object = none();
break;
case return_value_policy::reference_internal:
// Default should do the right thing
if (!parent) {
pybind11_fail("Cannot use reference internal when there is no parent");
}
parent_object = reinterpret_borrow<object>(parent);
break;
case return_value_policy::take_ownership:
delete src;
// fallthrough
default:
// move, take_ownership don't make any sense for a ref/map:
pybind11_fail("Invalid return_value_policy for Eigen Map type, must be either "
"reference or reference_internal");
}
auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
convert_dsizes_to_vector(Helper::get_shape(*src)),
src->data(),
std::move(parent_object));
if (!writeable) {
array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
}
return result.release();
}
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
static constexpr bool needs_writeable = !std::is_const<typename std::remove_pointer<
typename get_storage_pointer_type<MapType>::SPT>::type>::value;
#else
// Handle Eigen bug
static constexpr bool needs_writeable = !std::is_const<Type>::value;
#endif
protected:
// TODO: Move to std::optional once std::optional has more support
std::unique_ptr<MapType> value;
public:
static constexpr auto name = get_tensor_descriptor<Type, true, needs_writeable>::value;
explicit operator MapType *() { return value.get(); }
explicit operator MapType &() { return *value; }
explicit operator MapType &&() && { return std::move(*value); }
template <typename T_>
using cast_op_type = ::pybind11::detail::movable_cast_op_type<T_>;
};
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -12,16 +12,23 @@
#include "pybind11.h" #include "pybind11.h"
#include "eval.h" #include "eval.h"
#include <memory>
#include <vector>
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
# error Embedding the interpreter is not supported with PyPy # error Embedding the interpreter is not supported with PyPy
#endif #endif
#define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ #if PY_MAJOR_VERSION >= 3
extern "C" PyObject *pybind11_init_impl_##name(); \ # define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); } extern "C" PyObject *pybind11_init_impl_##name(); \
extern "C" PyObject *pybind11_init_impl_##name() { \
return pybind11_init_wrapper_##name(); \
}
#else
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
extern "C" void pybind11_init_impl_##name(); \
extern "C" void pybind11_init_impl_##name() { \
pybind11_init_wrapper_##name(); \
}
#endif
/** \rst /** \rst
Add a new module to the table of builtins for the interpreter. Must be Add a new module to the table of builtins for the interpreter. Must be
@ -38,173 +45,69 @@
}); });
} }
\endrst */ \endrst */
#define PYBIND11_EMBEDDED_MODULE(name, variable) \ #define PYBIND11_EMBEDDED_MODULE(name, variable) \
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \ static ::pybind11::module_::module_def \
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \ PYBIND11_CONCAT(pybind11_module_def_, name); \
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
auto m = ::pybind11::module_::create_extension_module( \ static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \ auto m = ::pybind11::module_::create_extension_module( \
try { \ PYBIND11_TOSTRING(name), nullptr, \
PYBIND11_CONCAT(pybind11_init_, name)(m); \ &PYBIND11_CONCAT(pybind11_module_def_, name)); \
return m.ptr(); \ try { \
} \ PYBIND11_CONCAT(pybind11_init_, name)(m); \
PYBIND11_CATCH_INIT_EXCEPTIONS \ return m.ptr(); \
} \ } PYBIND11_CATCH_INIT_EXCEPTIONS \
PYBIND11_EMBEDDED_MODULE_IMPL(name) \ } \
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \ PYBIND11_EMBEDDED_MODULE_IMPL(name) \
PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \ ::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \ (PYBIND11_TOSTRING(name), \
& variable) // NOLINT(bugprone-macro-parentheses) PYBIND11_CONCAT(pybind11_init_impl_, name)); \
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. /// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
struct embedded_module { struct embedded_module {
using init_t = PyObject *(*) (); #if PY_MAJOR_VERSION >= 3
using init_t = PyObject *(*)();
#else
using init_t = void (*)();
#endif
embedded_module(const char *name, init_t init) { embedded_module(const char *name, init_t init) {
if (Py_IsInitialized() != 0) { if (Py_IsInitialized())
pybind11_fail("Can't add new modules after the interpreter has been initialized"); pybind11_fail("Can't add new modules after the interpreter has been initialized");
}
auto result = PyImport_AppendInittab(name, init); auto result = PyImport_AppendInittab(name, init);
if (result == -1) { if (result == -1)
pybind11_fail("Insufficient memory to add a new module"); pybind11_fail("Insufficient memory to add a new module");
}
} }
}; };
struct wide_char_arg_deleter {
void operator()(wchar_t *ptr) const {
// API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
PyMem_RawFree(ptr);
}
};
inline wchar_t *widen_chars(const char *safe_arg) {
wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr);
return widened_arg;
}
inline void precheck_interpreter() {
if (Py_IsInitialized() != 0) {
pybind11_fail("The interpreter is already running");
}
}
#if !defined(PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX)
# define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX (0x03080000)
#endif
#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers,
int argc,
const char *const *argv,
bool add_program_dir_to_path) {
detail::precheck_interpreter();
Py_InitializeEx(init_signal_handlers ? 1 : 0);
# if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000
PyEval_InitThreads();
# endif
// Before it was special-cased in python 3.8, passing an empty or null argv
// caused a segfault, so we have to reimplement the special case ourselves.
bool special_case = (argv == nullptr || argc <= 0);
const char *const empty_argv[]{"\0"};
const char *const *safe_argv = special_case ? empty_argv : argv;
if (special_case) {
argc = 1;
}
auto argv_size = static_cast<size_t>(argc);
// SetArgv* on python 3 takes wchar_t, so we have to convert.
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
std::vector<std::unique_ptr<wchar_t[], detail::wide_char_arg_deleter>> widened_argv_entries;
widened_argv_entries.reserve(argv_size);
for (size_t ii = 0; ii < argv_size; ++ii) {
widened_argv_entries.emplace_back(detail::widen_chars(safe_argv[ii]));
if (!widened_argv_entries.back()) {
// A null here indicates a character-encoding failure or the python
// interpreter out of memory. Give up.
return;
}
widened_argv[ii] = widened_argv_entries.back().get();
}
auto *pysys_argv = widened_argv.get();
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
}
#endif
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
inline void initialize_interpreter(PyConfig *config,
int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
detail::precheck_interpreter();
PyStatus status = PyConfig_SetBytesArgv(config, argc, const_cast<char *const *>(argv));
if (PyStatus_Exception(status) != 0) {
// A failure here indicates a character-encoding failure or the python
// interpreter out of memory. Give up.
PyConfig_Clear(config);
throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
: "Failed to prepare CPython");
}
status = Py_InitializeFromConfig(config);
if (PyStatus_Exception(status) != 0) {
PyConfig_Clear(config);
throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
: "Failed to init CPython");
}
if (add_program_dir_to_path) {
PyRun_SimpleString("import sys, os.path; "
"sys.path.insert(0, "
"os.path.abspath(os.path.dirname(sys.argv[0])) "
"if sys.argv and os.path.exists(sys.argv[0]) else '')");
}
PyConfig_Clear(config);
}
#endif
/** \rst /** \rst
Initialize the Python interpreter. No other pybind11 or CPython API functions can be Initialize the Python interpreter. No other pybind11 or CPython API functions can be
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
optional `init_signal_handlers` parameter can be used to skip the registration of optional parameter can be used to skip the registration of signal handlers (see the
signal handlers (see the `Python documentation`_ for details). Calling this function `Python documentation`_ for details). Calling this function again after the interpreter
again after the interpreter has already been initialized is a fatal error. has already been initialized is a fatal error.
If initializing the Python interpreter fails, then the program is terminated. (This If initializing the Python interpreter fails, then the program is terminated. (This
is controlled by the CPython runtime and is an exception to pybind11's normal behavior is controlled by the CPython runtime and is an exception to pybind11's normal behavior
of throwing exceptions on errors.) of throwing exceptions on errors.)
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
used to populate ``sys.argv`` and ``sys.path``.
See the |PySys_SetArgvEx documentation|_ for details.
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
\endrst */ \endrst */
inline void initialize_interpreter(bool init_signal_handlers = true, inline void initialize_interpreter(bool init_signal_handlers = true) {
int argc = 0, if (Py_IsInitialized())
const char *const *argv = nullptr, pybind11_fail("The interpreter is already running");
bool add_program_dir_to_path = true) {
#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
detail::initialize_interpreter_pre_pyconfig(
init_signal_handlers, argc, argv, add_program_dir_to_path);
#else
PyConfig config;
PyConfig_InitPythonConfig(&config);
// See PR #4473 for background
config.parse_argv = 0;
config.install_signal_handlers = init_signal_handlers ? 1 : 0; Py_InitializeEx(init_signal_handlers ? 1 : 0);
initialize_interpreter(&config, argc, argv, add_program_dir_to_path);
#endif // Make .py files in the working directory available by default
module_::import("sys").attr("path").cast<list>().append(".");
} }
/** \rst /** \rst
@ -243,19 +146,16 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
\endrst */ \endrst */
inline void finalize_interpreter() { inline void finalize_interpreter() {
handle builtins(PyEval_GetBuiltins());
const char *id = PYBIND11_INTERNALS_ID;
// Get the internals pointer (without creating it if it doesn't exist). It's possible for the // Get the internals pointer (without creating it if it doesn't exist). It's possible for the
// internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()`
// during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). // during destruction), so we get the pointer-pointer here and check it after Py_Finalize().
detail::internals **internals_ptr_ptr = detail::get_internals_pp(); detail::internals **internals_ptr_ptr = detail::get_internals_pp();
// It could also be stashed in state_dict, so look there too: // It could also be stashed in builtins, so look there too:
if (object internals_obj if (builtins.contains(id) && isinstance<capsule>(builtins[id]))
= get_internals_obj_from_state_dict(detail::get_python_state_dict())) { internals_ptr_ptr = capsule(builtins[id]);
internals_ptr_ptr = detail::get_internals_pp_from_capsule(internals_obj);
}
// Local internals contains data managed by the current interpreter, so we must clear them to
// avoid undefined behaviors when initializing another interpreter
detail::get_local_internals().registered_types_cpp.clear();
detail::get_local_internals().registered_exception_translators.clear();
Py_Finalize(); Py_Finalize();
@ -269,8 +169,6 @@ inline void finalize_interpreter() {
Scope guard version of `initialize_interpreter` and `finalize_interpreter`. Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
This a move-only guard and only a single instance can exist. This a move-only guard and only a single instance can exist.
See `initialize_interpreter` for a discussion of its constructor arguments.
.. code-block:: cpp .. code-block:: cpp
#include <pybind11/embed.h> #include <pybind11/embed.h>
@ -282,31 +180,18 @@ inline void finalize_interpreter() {
\endrst */ \endrst */
class scoped_interpreter { class scoped_interpreter {
public: public:
explicit scoped_interpreter(bool init_signal_handlers = true, scoped_interpreter(bool init_signal_handlers = true) {
int argc = 0, initialize_interpreter(init_signal_handlers);
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
} }
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
explicit scoped_interpreter(PyConfig *config,
int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
initialize_interpreter(config, argc, argv, add_program_dir_to_path);
}
#endif
scoped_interpreter(const scoped_interpreter &) = delete; scoped_interpreter(const scoped_interpreter &) = delete;
scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; }
scoped_interpreter &operator=(const scoped_interpreter &) = delete; scoped_interpreter &operator=(const scoped_interpreter &) = delete;
scoped_interpreter &operator=(scoped_interpreter &&) = delete; scoped_interpreter &operator=(scoped_interpreter &&) = delete;
~scoped_interpreter() { ~scoped_interpreter() {
if (is_valid) { if (is_valid)
finalize_interpreter(); finalize_interpreter();
}
} }
private: private:

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