mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
use pybind11 instead of raw C Python API, python_streambuffer is broken for now
This commit is contained in:
parent
eaaa310cef
commit
de0e010056
@ -31,7 +31,7 @@ if(TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(ARROW)
|
||||
if(PYTHON)
|
||||
add_subdirectory(xlntpyarrow)
|
||||
endif()
|
||||
|
||||
|
133
cmake/FindArrow.cmake
Normal file
133
cmake/FindArrow.cmake
Normal file
@ -0,0 +1,133 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# - Find ARROW (arrow/api.h, libarrow.a, libarrow.so)
|
||||
# This module defines
|
||||
# ARROW_INCLUDE_DIR, directory containing headers
|
||||
# ARROW_LIBS, directory containing arrow libraries
|
||||
# ARROW_STATIC_LIB, path to libarrow.a
|
||||
# ARROW_SHARED_LIB, path to libarrow's shared library
|
||||
# ARROW_SHARED_IMP_LIB, path to libarrow's import library (MSVC only)
|
||||
# ARROW_FOUND, whether arrow has been found
|
||||
|
||||
include(FindPkgConfig)
|
||||
|
||||
if ("$ENV{ARROW_HOME}" STREQUAL "")
|
||||
pkg_check_modules(ARROW arrow)
|
||||
if (ARROW_FOUND)
|
||||
pkg_get_variable(ARROW_ABI_VERSION arrow abi_version)
|
||||
message(STATUS "Arrow ABI version: ${ARROW_ABI_VERSION}")
|
||||
pkg_get_variable(ARROW_SO_VERSION arrow so_version)
|
||||
message(STATUS "Arrow SO version: ${ARROW_SO_VERSION}")
|
||||
set(ARROW_INCLUDE_DIR ${ARROW_INCLUDE_DIRS})
|
||||
set(ARROW_LIBS ${ARROW_LIBRARY_DIRS})
|
||||
set(ARROW_SEARCH_LIB_PATH ${ARROW_LIBRARY_DIRS})
|
||||
endif()
|
||||
else()
|
||||
set(ARROW_HOME "$ENV{ARROW_HOME}")
|
||||
|
||||
set(ARROW_SEARCH_HEADER_PATHS
|
||||
${ARROW_HOME}/include
|
||||
)
|
||||
|
||||
set(ARROW_SEARCH_LIB_PATH
|
||||
${ARROW_HOME}/lib
|
||||
)
|
||||
|
||||
find_path(ARROW_INCLUDE_DIR arrow/array.h PATHS
|
||||
${ARROW_SEARCH_HEADER_PATHS}
|
||||
# make sure we don't accidentally pick up a different version
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
endif()
|
||||
|
||||
find_library(ARROW_LIB_PATH NAMES arrow
|
||||
PATHS
|
||||
${ARROW_SEARCH_LIB_PATH}
|
||||
NO_DEFAULT_PATH)
|
||||
get_filename_component(ARROW_LIBS ${ARROW_LIB_PATH} DIRECTORY)
|
||||
|
||||
find_library(ARROW_PYTHON_LIB_PATH NAMES arrow_python
|
||||
PATHS
|
||||
${ARROW_SEARCH_LIB_PATH}
|
||||
NO_DEFAULT_PATH)
|
||||
get_filename_component(ARROW_PYTHON_LIBS ${ARROW_PYTHON_LIB_PATH} DIRECTORY)
|
||||
|
||||
if (MSVC)
|
||||
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".dll")
|
||||
|
||||
if (MSVC AND NOT ARROW_MSVC_STATIC_LIB_SUFFIX)
|
||||
set(ARROW_MSVC_STATIC_LIB_SUFFIX "_static")
|
||||
endif()
|
||||
|
||||
find_library(ARROW_SHARED_LIBRARIES NAMES arrow
|
||||
PATHS ${ARROW_HOME} NO_DEFAULT_PATH
|
||||
PATH_SUFFIXES "bin" )
|
||||
|
||||
find_library(ARROW_PYTHON_SHARED_LIBRARIES NAMES arrow_python
|
||||
PATHS ${ARROW_HOME} NO_DEFAULT_PATH
|
||||
PATH_SUFFIXES "bin" )
|
||||
get_filename_component(ARROW_SHARED_LIBS ${ARROW_SHARED_LIBRARIES} PATH )
|
||||
get_filename_component(ARROW_PYTHON_SHARED_LIBS ${ARROW_PYTHON_SHARED_LIBRARIES} PATH )
|
||||
endif ()
|
||||
|
||||
if (ARROW_INCLUDE_DIR AND ARROW_LIBS)
|
||||
set(ARROW_FOUND TRUE)
|
||||
set(ARROW_LIB_NAME arrow)
|
||||
set(ARROW_PYTHON_LIB_NAME arrow_python)
|
||||
if (MSVC)
|
||||
set(ARROW_STATIC_LIB ${ARROW_LIBS}/${ARROW_LIB_NAME}${ARROW_MSVC_STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
set(ARROW_PYTHON_STATIC_LIB ${ARROW_PYTHON_LIBS}/${ARROW_PYTHON_LIB_NAME}${ARROW_MSVC_STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
set(ARROW_SHARED_LIB ${ARROW_SHARED_LIBS}/${ARROW_LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
set(ARROW_PYTHON_SHARED_LIB ${ARROW_PYTHON_SHARED_LIBS}/${ARROW_PYTHON_LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
set(ARROW_SHARED_IMP_LIB ${ARROW_LIBS}/${ARROW_LIB_NAME}.lib)
|
||||
set(ARROW_PYTHON_SHARED_IMP_LIB ${ARROW_PYTHON_LIBS}/${ARROW_PYTHON_LIB_NAME}.lib)
|
||||
else()
|
||||
set(ARROW_STATIC_LIB ${ARROW_PYTHON_LIB_PATH}/lib${ARROW_LIB_NAME}.a)
|
||||
set(ARROW_PYTHON_STATIC_LIB ${ARROW_PYTHON_LIB_PATH}/lib${ARROW_PYTHON_LIB_NAME}.a)
|
||||
|
||||
set(ARROW_SHARED_LIB ${ARROW_LIBS}/lib${ARROW_LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
set(ARROW_PYTHON_SHARED_LIB ${ARROW_LIBS}/lib${ARROW_PYTHON_LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ARROW_FOUND)
|
||||
if (NOT Arrow_FIND_QUIETLY)
|
||||
message(STATUS "Found the Arrow core library: ${ARROW_LIB_PATH}")
|
||||
message(STATUS "Found the Arrow Python library: ${ARROW_PYTHON_LIB_PATH}")
|
||||
endif ()
|
||||
else ()
|
||||
if (NOT Arrow_FIND_QUIETLY)
|
||||
set(ARROW_ERR_MSG "Could not find the Arrow library. Looked for headers")
|
||||
set(ARROW_ERR_MSG "${ARROW_ERR_MSG} in ${ARROW_SEARCH_HEADER_PATHS}, and for libs")
|
||||
set(ARROW_ERR_MSG "${ARROW_ERR_MSG} in ${ARROW_SEARCH_LIB_PATH}")
|
||||
if (Arrow_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "${ARROW_ERR_MSG}")
|
||||
else (Arrow_FIND_REQUIRED)
|
||||
message(STATUS "${ARROW_ERR_MSG}")
|
||||
endif (Arrow_FIND_REQUIRED)
|
||||
endif ()
|
||||
set(ARROW_FOUND FALSE)
|
||||
endif ()
|
||||
|
||||
mark_as_advanced(
|
||||
ARROW_INCLUDE_DIR
|
||||
ARROW_STATIC_LIB
|
||||
ARROW_SHARED_LIB
|
||||
ARROW_PYTHON_STATIC_LIB
|
||||
ARROW_PYTHON_SHARED_LIB
|
||||
)
|
@ -530,7 +530,7 @@ struct stylesheet
|
||||
|
||||
workbook *parent;
|
||||
|
||||
bool garbage_collection_enabled = false;
|
||||
bool garbage_collection_enabled = true;
|
||||
|
||||
std::list<conditional_format_impl> conditional_format_impls;
|
||||
std::list<format_impl> format_impls;
|
||||
|
@ -34,6 +34,7 @@ class cell_test_suite : public test_suite
|
||||
public:
|
||||
cell_test_suite()
|
||||
{
|
||||
register_test(test_temp);
|
||||
register_test(test_infer_numeric);
|
||||
register_test(test_constructor);
|
||||
register_test(test_null);
|
||||
@ -69,6 +70,25 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void test_temp()
|
||||
{
|
||||
xlnt::workbook workbook;
|
||||
xlnt::worksheet worksheet = workbook.active_sheet();
|
||||
xlnt::alignment center;
|
||||
center.horizontal(xlnt::horizontal_alignment::center);
|
||||
center.vertical(xlnt::vertical_alignment::center);
|
||||
auto style = workbook.create_style("centered");
|
||||
style.alignment(center);
|
||||
|
||||
for (std::uint32_t row = 1; row < 7000; ++row)
|
||||
for (std::uint32_t column = 1; column < 13; ++column) {
|
||||
xlnt::cell cell = worksheet.cell(column, row);
|
||||
cell.value("test");
|
||||
cell.style(style);
|
||||
}
|
||||
|
||||
workbook.save("test.xlsx");
|
||||
}
|
||||
void test_infer_numeric()
|
||||
{
|
||||
xlnt::workbook wb;
|
||||
|
59
third-party/pybind11/.appveyor.yml
vendored
Normal file
59
third-party/pybind11/.appveyor.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
version: 1.0.{build}
|
||||
image:
|
||||
- Visual Studio 2017
|
||||
- Visual Studio 2015
|
||||
test: off
|
||||
build:
|
||||
parallel: true
|
||||
platform:
|
||||
- x64
|
||||
- x86
|
||||
environment:
|
||||
matrix:
|
||||
- CONDA: 36
|
||||
CPP: 14
|
||||
- CONDA: 27
|
||||
CPP: 14
|
||||
- CONDA: 36
|
||||
CPP: latest
|
||||
matrix:
|
||||
exclude:
|
||||
- image: Visual Studio 2015
|
||||
platform: x86
|
||||
- image: Visual Studio 2015
|
||||
CPP: latest
|
||||
- image: Visual Studio 2017
|
||||
CPP: latest
|
||||
platform: x86
|
||||
install:
|
||||
- ps: |
|
||||
if ($env:PLATFORM -eq "x64") { $env:CMAKE_ARCH = "x64" }
|
||||
if ($env:APPVEYOR_JOB_NAME -like "*Visual Studio 2017*") { $env:CMAKE_GENERATOR = "Visual Studio 15 2017" }
|
||||
else { $env:CMAKE_GENERATOR = "Visual Studio 14 2015" }
|
||||
if ($env:PYTHON) {
|
||||
if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" }
|
||||
$env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH"
|
||||
pip install --disable-pip-version-check --user --upgrade pip wheel
|
||||
pip install pytest numpy scipy
|
||||
} elseif ($env:CONDA) {
|
||||
if ($env:CONDA -eq "27") { $env:CONDA = "" }
|
||||
if ($env:PLATFORM -eq "x64") { $env:CONDA = "$env:CONDA-x64" }
|
||||
$env:PATH = "C:\Miniconda$env:CONDA\;C:\Miniconda$env:CONDA\Scripts\;$env:PATH"
|
||||
$env:PYTHONHOME = "C:\Miniconda$env:CONDA"
|
||||
conda install -y -q pytest numpy scipy
|
||||
}
|
||||
- ps: |
|
||||
Start-FileDownload 'http://bitbucket.org/eigen/eigen/get/3.3.3.zip'
|
||||
7z x 3.3.3.zip -y > $null
|
||||
$env:CMAKE_INCLUDE_PATH = "eigen-eigen-67e894c6cd8f"
|
||||
build_script:
|
||||
- cmake -G "%CMAKE_GENERATOR%" -A "%CMAKE_ARCH%"
|
||||
-DPYBIND11_CPP_STANDARD=/std:c++%CPP%
|
||||
-DPYBIND11_WERROR=ON
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
-DCMAKE_SUPPRESS_REGENERATION=1
|
||||
- set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
- cmake --build . --config Release --target pytest -- /v:m /logger:%MSBuildLogger%
|
||||
- cmake --build . --config Release --target cpptest -- /v:m /logger:%MSBuildLogger%
|
||||
- cmake --build . --config Release --target test_cmake_build -- /v:m /logger:%MSBuildLogger%
|
||||
on_failure: if exist "tests\test_cmake_build" type tests\test_cmake_build\*.log*
|
37
third-party/pybind11/.gitignore
vendored
Normal file
37
third-party/pybind11/.gitignore
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
.DS_Store
|
||||
*.so
|
||||
*.pyd
|
||||
*.dll
|
||||
*.sln
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*.filters
|
||||
example.dir
|
||||
Win32
|
||||
x64
|
||||
Release
|
||||
Debug
|
||||
.vs
|
||||
CTestTestfile.cmake
|
||||
Testing
|
||||
autogen
|
||||
MANIFEST
|
||||
/.ninja_*
|
||||
/*.ninja
|
||||
/docs/.build
|
||||
*.py[co]
|
||||
*.egg-info
|
||||
*~
|
||||
.DS_Store
|
||||
/dist
|
||||
/build
|
||||
/cmake/
|
||||
.cache/
|
||||
sosize-*.txt
|
||||
pybind11Config*.cmake
|
||||
pybind11Targets.cmake
|
3
third-party/pybind11/.gitmodules
vendored
Normal file
3
third-party/pybind11/.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "tools/clang"]
|
||||
path = tools/clang
|
||||
url = https://github.com/wjakob/clang-cindex-python3
|
3
third-party/pybind11/.readthedocs.yml
vendored
Normal file
3
third-party/pybind11/.readthedocs.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
python:
|
||||
version: 3
|
||||
requirements_file: docs/requirements.txt
|
200
third-party/pybind11/.travis.yml
vendored
Normal file
200
third-party/pybind11/.travis.yml
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
language: cpp
|
||||
dist: trusty
|
||||
sudo: false
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
env: PYTHON=2.7 CPP=11 GCC=4.8
|
||||
addons:
|
||||
apt:
|
||||
packages: [cmake=2.\*, cmake-data=2.\*]
|
||||
- os: linux
|
||||
env: PYTHON=3.6 CPP=11 GCC=4.8
|
||||
addons:
|
||||
apt:
|
||||
sources: [deadsnakes]
|
||||
packages: [python3.6-dev python3.6-venv, cmake=2.\*, cmake-data=2.\*]
|
||||
- sudo: true
|
||||
services: docker
|
||||
env: PYTHON=2.7 CPP=14 GCC=6
|
||||
- sudo: true
|
||||
services: docker
|
||||
env: PYTHON=3.5 CPP=14 GCC=6 DEBUG=1
|
||||
- sudo: true
|
||||
services: docker
|
||||
env: PYTHON=3.6 CPP=17 GCC=7
|
||||
- os: linux
|
||||
env: PYTHON=3.6 CPP=17 CLANG=4.0
|
||||
addons:
|
||||
apt:
|
||||
sources: [deadsnakes, llvm-toolchain-trusty-4.0]
|
||||
packages: [python3.6-dev python3.6-venv clang-4.0 llvm-4.0-dev, lld-4.0]
|
||||
- os: osx
|
||||
osx_image: xcode7.3
|
||||
env: PYTHON=2.7 CPP=14 CLANG
|
||||
- os: osx
|
||||
osx_image: xcode7.3
|
||||
env: PYTHON=3.6 CPP=14 CLANG
|
||||
# Test a PyPy 2.7 build
|
||||
- os: linux
|
||||
env: PYPY=5.8 PYTHON=2.7 CPP=11 GCC=4.8
|
||||
addons:
|
||||
apt:
|
||||
packages: [libblas-dev, liblapack-dev, gfortran]
|
||||
- sudo: true
|
||||
services: docker
|
||||
env: ARCH=i386 PYTHON=3.5 CPP=14 GCC=6
|
||||
# This next one does a make install *before* testing, then builds the tests against the installed version:
|
||||
- sudo: true
|
||||
services: docker
|
||||
env: PYTHON=3.5 CPP=14 CLANG=3.9 INSTALL=1
|
||||
script:
|
||||
- |
|
||||
$SCRIPT_RUN_PREFIX sh -c "set -e
|
||||
cmake ${CMAKE_EXTRA_ARGS} -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0
|
||||
make install
|
||||
cp -a tests /pybind11-tests
|
||||
mkdir /build-tests && cd /build-tests
|
||||
cmake ../pybind11-tests ${CMAKE_EXTRA_ARGS} -DPYBIND11_WERROR=ON
|
||||
make pytest -j 2"
|
||||
# A barebones build makes sure everything still works without optional deps (numpy/scipy/eigen)
|
||||
# and also tests the automatic discovery functions in CMake (Python version, C++ standard).
|
||||
- os: linux
|
||||
env: BAREBONES PYTHON=3.5
|
||||
install: $PY_CMD -m pip install --user --upgrade pytest
|
||||
# Documentation build:
|
||||
- os: linux
|
||||
language: docs
|
||||
env: DOCS STYLE LINT
|
||||
install:
|
||||
- export PATH="~/.local/bin:$PATH"
|
||||
- $PY_CMD -m pip install --user --upgrade sphinx sphinx_rtd_theme breathe flake8 pep8-naming
|
||||
- |
|
||||
curl -fsSL ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.12.linux.bin.tar.gz | tar xz
|
||||
export PATH="$PWD/doxygen-1.8.12/bin:$PATH"
|
||||
script:
|
||||
- make -C docs html SPHINX_OPTIONS=-W
|
||||
- tools/check-style.sh
|
||||
- flake8
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.local/bin
|
||||
- $HOME/.local/lib
|
||||
- $HOME/.local/include
|
||||
- $HOME/Library/Python
|
||||
before_install:
|
||||
- |
|
||||
# Configure build variables
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
if [ -n "$CLANG" ]; then
|
||||
export CXX=clang++-$CLANG CC=clang-$CLANG
|
||||
COMPILER_PACKAGES="clang-$CLANG llvm-$CLANG-dev"
|
||||
else
|
||||
if [ -z "$GCC" ]; then GCC=4.8
|
||||
else COMPILER_PACKAGES=g++-$GCC
|
||||
fi
|
||||
export CXX=g++-$GCC CC=gcc-$GCC
|
||||
fi
|
||||
if [ "$GCC" = "6" ] || [ "$CLANG" = "3.9" ]; then DOCKER=${ARCH:+$ARCH/}debian:stretch
|
||||
elif [ "$GCC" = "7" ]; then DOCKER=debian:buster
|
||||
fi
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
export CXX=clang++ CC=clang;
|
||||
fi
|
||||
if [ -n "$CPP" ]; then CPP=-std=c++$CPP; fi
|
||||
if [ "${PYTHON:0:1}" = "3" ]; then PY=3; fi
|
||||
if [ -n "$DEBUG" ]; then CMAKE_EXTRA_ARGS="${CMAKE_EXTRA_ARGS} -DCMAKE_BUILD_TYPE=Debug"; fi
|
||||
- |
|
||||
# Initialize environment
|
||||
set -e
|
||||
if [ -n "$DOCKER" ]; then
|
||||
docker pull $DOCKER
|
||||
|
||||
containerid=$(docker run --detach --tty \
|
||||
--volume="$PWD":/pybind11 --workdir=/pybind11 \
|
||||
--env="CC=$CC" --env="CXX=$CXX" --env="DEBIAN_FRONTEND=$DEBIAN_FRONTEND" \
|
||||
--env=GCC_COLORS=\ \
|
||||
$DOCKER)
|
||||
SCRIPT_RUN_PREFIX="docker exec --tty $containerid"
|
||||
$SCRIPT_RUN_PREFIX sh -c 'for s in 0 15; do sleep $s; apt-get update && apt-get -qy dist-upgrade && break; done'
|
||||
else
|
||||
if [ "$PYPY" = "5.8" ]; then
|
||||
curl -fSL https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux64.tar.bz2 | tar xj
|
||||
PY_CMD=$(echo `pwd`/pypy2-v5.8.0-linux64/bin/pypy)
|
||||
CMAKE_EXTRA_ARGS="${CMAKE_EXTRA_ARGS} -DPYTHON_EXECUTABLE:FILEPATH=$PY_CMD"
|
||||
else
|
||||
PY_CMD=python$PYTHON
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
if [ "$PY" = "3" ]; then
|
||||
brew update; brew install python$PY;
|
||||
else
|
||||
curl -fsSL https://bootstrap.pypa.io/get-pip.py | $PY_CMD - --user
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ "$PY" = 3 ] || [ -n "$PYPY" ]; then
|
||||
$PY_CMD -m ensurepip --user
|
||||
fi
|
||||
$PY_CMD -m pip install --user --upgrade pip wheel
|
||||
fi
|
||||
set +e
|
||||
install:
|
||||
- |
|
||||
# Install dependencies
|
||||
set -e
|
||||
if [ -n "$DOCKER" ]; then
|
||||
if [ -n "$DEBUG" ]; then
|
||||
PY_DEBUG="python$PYTHON-dbg python$PY-scipy-dbg"
|
||||
CMAKE_EXTRA_ARGS="${CMAKE_EXTRA_ARGS} -DPYTHON_EXECUTABLE=/usr/bin/python${PYTHON}dm"
|
||||
fi
|
||||
$SCRIPT_RUN_PREFIX sh -c "for s in 0 15; do sleep \$s; \
|
||||
apt-get -qy --no-install-recommends install \
|
||||
$PY_DEBUG python$PYTHON-dev python$PY-pytest python$PY-scipy \
|
||||
libeigen3-dev cmake make ${COMPILER_PACKAGES} && break; done"
|
||||
else
|
||||
|
||||
if [ "$CLANG" = "4.0" ]; then
|
||||
if ! [ -d ~/.local/include/c++/v1 ]; then
|
||||
# Neither debian nor llvm provide a libc++ 4.0 deb; luckily it's fairly quick
|
||||
# to build, install (and cache), so do it ourselves:
|
||||
git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm-source
|
||||
git clone https://github.com/llvm-mirror/libcxx.git llvm-source/projects/libcxx -b release_40
|
||||
git clone https://github.com/llvm-mirror/libcxxabi.git llvm-source/projects/libcxxabi -b release_40
|
||||
mkdir llvm-build && cd llvm-build
|
||||
# Building llvm requires a newer cmake than is provided by the trusty container:
|
||||
CMAKE=cmake-3.8.0-Linux-x86_64
|
||||
curl https://cmake.org/files/v3.8/$CMAKE.tar.gz | tar xz
|
||||
./$CMAKE/bin/cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/.local ../llvm-source
|
||||
make -j2 install-cxxabi install-cxx
|
||||
cp -a include/c++/v1/*cxxabi*.h ~/.local/include/c++/v1
|
||||
cd ..
|
||||
fi
|
||||
export CXXFLAGS="-isystem $HOME/.local/include/c++/v1 -stdlib=libc++"
|
||||
export LDFLAGS="-L$HOME/.local/lib -fuse-ld=lld-$CLANG"
|
||||
export LD_LIBRARY_PATH="$HOME/.local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
if [ "$CPP" = "-std=c++17" ]; then CPP="-std=c++1z"; fi
|
||||
fi
|
||||
|
||||
export NPY_NUM_BUILD_JOBS=2
|
||||
echo "Installing pytest, numpy, scipy..."
|
||||
${PYPY:+travis_wait 30} $PY_CMD -m pip install --user --upgrade pytest numpy scipy \
|
||||
${PYPY:+--extra-index-url https://imaginary.ca/trusty-pypi}
|
||||
echo "done."
|
||||
|
||||
wget -q -O eigen.tar.gz https://bitbucket.org/eigen/eigen/get/3.3.3.tar.gz
|
||||
tar xzf eigen.tar.gz
|
||||
export CMAKE_INCLUDE_PATH="${CMAKE_INCLUDE_PATH:+:}$PWD/eigen-eigen-67e894c6cd8f"
|
||||
fi
|
||||
set +e
|
||||
script:
|
||||
- $SCRIPT_RUN_PREFIX cmake ${CMAKE_EXTRA_ARGS}
|
||||
-DPYBIND11_PYTHON_VERSION=$PYTHON
|
||||
-DPYBIND11_CPP_STANDARD=$CPP
|
||||
-DPYBIND11_WERROR=${WERROR:-ON}
|
||||
-DDOWNLOAD_CATCH=ON
|
||||
- $SCRIPT_RUN_PREFIX make pytest -j 2
|
||||
- $SCRIPT_RUN_PREFIX make cpptest -j 2
|
||||
- $SCRIPT_RUN_PREFIX make test_cmake_build
|
||||
after_failure: cat tests/test_cmake_build/*.log*
|
||||
after_script:
|
||||
- if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi
|
139
third-party/pybind11/CMakeLists.txt
vendored
Normal file
139
third-party/pybind11/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
# CMakeLists.txt -- Build system for the pybind11 modules
|
||||
#
|
||||
# Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
#
|
||||
# All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
if (POLICY CMP0048)
|
||||
# cmake warns if loaded from a min-3.0-required parent dir, so silence the warning:
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
endif()
|
||||
|
||||
project(pybind11)
|
||||
|
||||
# Check if pybind11 is being used directly or via add_subdirectory
|
||||
set(PYBIND11_MASTER_PROJECT OFF)
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(PYBIND11_MASTER_PROJECT ON)
|
||||
endif()
|
||||
|
||||
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
|
||||
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/tools")
|
||||
|
||||
include(pybind11Tools)
|
||||
|
||||
# Cache variables so pybind11_add_module can be used in parent projects
|
||||
set(PYBIND11_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/include" CACHE INTERNAL "")
|
||||
set(PYTHON_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} CACHE INTERNAL "")
|
||||
set(PYTHON_LIBRARIES ${PYTHON_LIBRARIES} CACHE INTERNAL "")
|
||||
set(PYTHON_MODULE_PREFIX ${PYTHON_MODULE_PREFIX} CACHE INTERNAL "")
|
||||
set(PYTHON_MODULE_EXTENSION ${PYTHON_MODULE_EXTENSION} CACHE INTERNAL "")
|
||||
|
||||
# NB: when adding a header don't forget to also add it to setup.py
|
||||
set(PYBIND11_HEADERS
|
||||
include/pybind11/attr.h
|
||||
include/pybind11/buffer_info.h
|
||||
include/pybind11/cast.h
|
||||
include/pybind11/chrono.h
|
||||
include/pybind11/class_support.h
|
||||
include/pybind11/common.h
|
||||
include/pybind11/complex.h
|
||||
include/pybind11/descr.h
|
||||
include/pybind11/options.h
|
||||
include/pybind11/eigen.h
|
||||
include/pybind11/embed.h
|
||||
include/pybind11/eval.h
|
||||
include/pybind11/functional.h
|
||||
include/pybind11/numpy.h
|
||||
include/pybind11/operators.h
|
||||
include/pybind11/pybind11.h
|
||||
include/pybind11/pytypes.h
|
||||
include/pybind11/stl.h
|
||||
include/pybind11/stl_bind.h
|
||||
include/pybind11/typeid.h
|
||||
)
|
||||
string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/"
|
||||
PYBIND11_HEADERS "${PYBIND11_HEADERS}")
|
||||
|
||||
if (PYBIND11_TEST)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# extract project version from source
|
||||
file(STRINGS "${PYBIND11_INCLUDE_DIR}/pybind11/common.h" pybind11_version_defines
|
||||
REGEX "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) ")
|
||||
foreach(ver ${pybind11_version_defines})
|
||||
if (ver MATCHES "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$")
|
||||
set(PYBIND11_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "")
|
||||
endif()
|
||||
endforeach()
|
||||
set(${PROJECT_NAME}_VERSION ${PYBIND11_VERSION_MAJOR}.${PYBIND11_VERSION_MINOR}.${PYBIND11_VERSION_PATCH})
|
||||
message(STATUS "pybind11 v${${PROJECT_NAME}_VERSION}")
|
||||
|
||||
option (USE_PYTHON_INCLUDE_DIR "Install pybind11 headers in Python include directory instead of default installation prefix" OFF)
|
||||
if (USE_PYTHON_INCLUDE_DIR)
|
||||
file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) # CMake >= 3.0
|
||||
# Build an interface library target:
|
||||
add_library(pybind11 INTERFACE)
|
||||
add_library(pybind11::pybind11 ALIAS pybind11) # to match exported target
|
||||
target_include_directories(pybind11 INTERFACE $<BUILD_INTERFACE:${PYBIND11_INCLUDE_DIR}>
|
||||
$<BUILD_INTERFACE:${PYTHON_INCLUDE_DIRS}>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
target_compile_options(pybind11 INTERFACE $<BUILD_INTERFACE:${PYBIND11_CPP_STANDARD}>)
|
||||
|
||||
add_library(module INTERFACE)
|
||||
add_library(pybind11::module ALIAS module)
|
||||
target_link_libraries(module INTERFACE pybind11::pybind11)
|
||||
if(WIN32 OR CYGWIN)
|
||||
target_link_libraries(module INTERFACE $<BUILD_INTERFACE:${PYTHON_LIBRARIES}>)
|
||||
elseif(APPLE)
|
||||
target_link_libraries(module INTERFACE "-undefined dynamic_lookup")
|
||||
endif()
|
||||
|
||||
add_library(embed INTERFACE)
|
||||
add_library(pybind11::embed ALIAS embed)
|
||||
target_link_libraries(embed INTERFACE pybind11::pybind11 $<BUILD_INTERFACE:${PYTHON_LIBRARIES}>)
|
||||
endif()
|
||||
|
||||
if (PYBIND11_INSTALL)
|
||||
install(FILES ${PYBIND11_HEADERS}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/pybind11)
|
||||
# GNUInstallDirs "DATADIR" wrong here; CMake search path wants "share".
|
||||
set(PYBIND11_CMAKECONFIG_INSTALL_DIR "share/cmake/${PROJECT_NAME}" CACHE STRING "install path for pybind11Config.cmake")
|
||||
|
||||
configure_package_config_file(tools/${PROJECT_NAME}Config.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
|
||||
# Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does
|
||||
# not depend on architecture specific settings or libraries.
|
||||
set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
|
||||
unset(CMAKE_SIZEOF_VOID_P)
|
||||
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
|
||||
VERSION ${${PROJECT_NAME}_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion)
|
||||
set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P})
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
|
||||
tools/FindPythonLibsNew.cmake
|
||||
tools/pybind11Tools.cmake
|
||||
DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
|
||||
|
||||
if(NOT (CMAKE_VERSION VERSION_LESS 3.0))
|
||||
install(TARGETS pybind11 module embed
|
||||
EXPORT "${PROJECT_NAME}Targets")
|
||||
install(EXPORT "${PROJECT_NAME}Targets"
|
||||
NAMESPACE "${PROJECT_NAME}::"
|
||||
DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
|
||||
endif()
|
||||
endif()
|
37
third-party/pybind11/CONTRIBUTING.md
vendored
Normal file
37
third-party/pybind11/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
Thank you for your interest in this project! Please refer to the following
|
||||
sections on how to contribute code and bug reports.
|
||||
|
||||
### Reporting bugs
|
||||
|
||||
At the moment, this project is run in the spare time of a single person
|
||||
([Wenzel Jakob](http://rgl.epfl.ch/people/wjakob)) with very limited resources
|
||||
for issue tracker tickets. Thus, before submitting a question or bug report,
|
||||
please take a moment of your time and ensure that your issue isn't already
|
||||
discussed in the project documentation provided at
|
||||
[http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest).
|
||||
|
||||
Assuming that you have identified a previously unknown problem or an important
|
||||
question, it's essential that you submit a self-contained and minimal piece of
|
||||
code that reproduces the problem. In other words: 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 on my end.
|
||||
|
||||
## Pull requests
|
||||
Contributions are submitted, reviewed, and accepted using Github pull requests.
|
||||
Please refer to [this
|
||||
article](https://help.github.com/articles/using-pull-requests) for details and
|
||||
adhere to the following rules to make the process as smooth as possible:
|
||||
|
||||
* Make a new branch for every feature you're working on.
|
||||
* Make small and clean pull requests that are easy to review but make sure they
|
||||
do add value by themselves.
|
||||
* Add tests for any new functionality and run the test suite (``make pytest``)
|
||||
to ensure that no existing features break.
|
||||
* This project has a strong focus on providing general solutions using a
|
||||
minimal amount of code, thus small pull requests are greatly preferred.
|
||||
|
||||
### License
|
||||
|
||||
pybind11 is provided under a BSD-style license that can be found in the
|
||||
``LICENSE`` file. By using, distributing, or contributing to this project, you
|
||||
agree to the terms and conditions of this license.
|
17
third-party/pybind11/ISSUE_TEMPLATE.md
vendored
Normal file
17
third-party/pybind11/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
Make sure you've completed the following steps before submitting your issue -- thank you!
|
||||
|
||||
1. Check if your question has already been answered in the [FAQ](http://pybind11.readthedocs.io/en/latest/faq.html) section.
|
||||
2. Make sure you've read the [documentation](http://pybind11.readthedocs.io/en/latest/). 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](https://gitter.im/pybind/Lobby).
|
||||
4. If you have a genuine bug report or a more complex question which is not answered in the previous items (or not suitable for chat), please fill in the details below.
|
||||
5. 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.
|
||||
|
||||
*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.)
|
36
third-party/pybind11/LICENSE
vendored
Normal file
36
third-party/pybind11/LICENSE
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You are under no obligation whatsoever to provide any bug fixes, patches, or
|
||||
upgrades to the features, functionality or performance of the source code
|
||||
("Enhancements") to anyone; however, if you choose to make your Enhancements
|
||||
available either publicly, or directly to the author of this software, without
|
||||
imposing a separate written license agreement for such Enhancements, then you
|
||||
hereby grant the following license: a non-exclusive, royalty-free perpetual
|
||||
license to install, use, modify, prepare derivative works, incorporate into
|
||||
other computer software, distribute, and sublicense such enhancements or
|
||||
derivative works thereof, in binary and source code form.
|
2
third-party/pybind11/MANIFEST.in
vendored
Normal file
2
third-party/pybind11/MANIFEST.in
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
include include/pybind11/*.h
|
||||
include LICENSE README.md CONTRIBUTING.md
|
129
third-party/pybind11/README.md
vendored
Normal file
129
third-party/pybind11/README.md
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
![pybind11 logo](https://github.com/pybind/pybind11/raw/master/docs/pybind11-logo.png)
|
||||
|
||||
# pybind11 — Seamless operability between C++11 and Python
|
||||
|
||||
[![Documentation Status](https://readthedocs.org/projects/pybind11/badge/?version=master)](http://pybind11.readthedocs.org/en/master/?badge=master)
|
||||
[![Documentation Status](https://readthedocs.org/projects/pybind11/badge/?version=stable)](http://pybind11.readthedocs.org/en/stable/?badge=stable)
|
||||
[![Gitter chat](https://img.shields.io/gitter/room/gitterHQ/gitter.svg)](https://gitter.im/pybind/Lobby)
|
||||
[![Build Status](https://travis-ci.org/pybind/pybind11.svg?branch=master)](https://travis-ci.org/pybind/pybind11)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true)](https://ci.appveyor.com/project/wjakob/pybind11)
|
||||
|
||||
**pybind11** is a lightweight header-only library that exposes C++ types in Python
|
||||
and vice versa, mainly to create Python bindings of existing C++ code. Its
|
||||
goals and syntax are similar to the excellent
|
||||
[Boost.Python](http://www.boost.org/doc/libs/1_58_0/libs/python/doc/) library
|
||||
by David Abrahams: to minimize boilerplate code in traditional extension
|
||||
modules by inferring type information using compile-time introspection.
|
||||
|
||||
The main issue with Boost.Python—and the reason for creating such a similar
|
||||
project—is Boost. Boost is an enormously large and complex suite of utility
|
||||
libraries that works with almost every C++ compiler in existence. This
|
||||
compatibility has its cost: arcane template tricks and workarounds are
|
||||
necessary to support the oldest and buggiest of compiler specimens. Now that
|
||||
C++11-compatible compilers are widely available, this heavy machinery has
|
||||
become an excessively large and unnecessary dependency.
|
||||
|
||||
Think of this library as a tiny self-contained version of Boost.Python with
|
||||
everything stripped away that isn't relevant for binding generation. Without
|
||||
comments, the core header files only require ~4K lines of code and depend on
|
||||
Python (2.7 or 3.x, or PyPy2.7 >= 5.7) and the C++ standard library. This
|
||||
compact implementation was possible thanks to some of the new C++11 language
|
||||
features (specifically: tuples, lambda functions and variadic templates). Since
|
||||
its creation, this library has grown beyond Boost.Python in many ways, leading
|
||||
to dramatically simpler binding code in many common situations.
|
||||
|
||||
Tutorial and reference documentation is provided at
|
||||
[http://pybind11.readthedocs.org/en/master](http://pybind11.readthedocs.org/en/master).
|
||||
A PDF version of the manual is available
|
||||
[here](https://media.readthedocs.org/pdf/pybind11/master/pybind11.pdf).
|
||||
|
||||
## Core features
|
||||
pybind11 can map the following core C++ features to Python
|
||||
|
||||
- Functions accepting and returning custom data structures per value, reference, or pointer
|
||||
- Instance methods and static methods
|
||||
- Overloaded functions
|
||||
- Instance attributes and static attributes
|
||||
- Arbitrary exception types
|
||||
- Enumerations
|
||||
- Callbacks
|
||||
- Iterators and ranges
|
||||
- Custom operators
|
||||
- Single and multiple inheritance
|
||||
- STL data structures
|
||||
- Iterators and ranges
|
||||
- Smart pointers with reference counting like ``std::shared_ptr``
|
||||
- Internal references with correct reference counting
|
||||
- C++ classes with virtual (and pure virtual) methods can be extended in Python
|
||||
|
||||
## Goodies
|
||||
In addition to the core functionality, pybind11 provides some extra goodies:
|
||||
|
||||
- Python 2.7, 3.x, and PyPy (PyPy2.7 >= 5.7) are supported with an
|
||||
implementation-agnostic interface.
|
||||
|
||||
- It is possible to bind C++11 lambda functions with captured variables. The
|
||||
lambda capture data is stored inside the resulting Python function object.
|
||||
|
||||
- pybind11 uses C++11 move constructors and move assignment operators whenever
|
||||
possible to efficiently transfer custom data types.
|
||||
|
||||
- It's easy to expose the internal storage of custom data types through
|
||||
Pythons' buffer protocols. This is handy e.g. for fast conversion between
|
||||
C++ matrix classes like Eigen and NumPy without expensive copy operations.
|
||||
|
||||
- pybind11 can automatically vectorize functions so that they are transparently
|
||||
applied to all entries of one or more NumPy array arguments.
|
||||
|
||||
- Python's slice-based access and assignment operations can be supported with
|
||||
just a few lines of code.
|
||||
|
||||
- Everything is contained in just a few header files; there is no need to link
|
||||
against any additional libraries.
|
||||
|
||||
- Binaries are generally smaller by a factor of at least 2 compared to
|
||||
equivalent bindings generated by Boost.Python. A recent pybind11 conversion
|
||||
of PyRosetta, an enormous Boost.Python binding project,
|
||||
[reported](http://graylab.jhu.edu/RosettaCon2016/PyRosetta-4.pdf) a binary
|
||||
size reduction of **5.4x** and compile time reduction by **5.8x**.
|
||||
|
||||
- When supported by the compiler, two new C++14 features (relaxed constexpr and
|
||||
return value deduction) are used to precompute function signatures at compile
|
||||
time, leading to smaller binaries.
|
||||
|
||||
- With little extra effort, C++ types can be pickled and unpickled similar to
|
||||
regular Python objects.
|
||||
|
||||
## Supported compilers
|
||||
|
||||
1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or newer)
|
||||
2. GCC 4.8 or newer
|
||||
3. Microsoft Visual Studio 2015 Update 3 or newer
|
||||
4. Intel C++ compiler 16 or newer (15 with a [workaround](https://github.com/pybind/pybind11/issues/276))
|
||||
5. Cygwin/GCC (tested on 2.5.1)
|
||||
|
||||
## About
|
||||
|
||||
This project was created by [Wenzel Jakob](http://rgl.epfl.ch/people/wjakob).
|
||||
Significant features and/or improvements to the code were contributed by
|
||||
Jonas Adler,
|
||||
Sylvain Corlay,
|
||||
Trent Houliston,
|
||||
Axel Huebl,
|
||||
@hulucc,
|
||||
Sergey Lyskov
|
||||
Johan Mabille,
|
||||
Tomasz Miąsko,
|
||||
Dean Moldovan,
|
||||
Ben Pritchard,
|
||||
Jason Rhinelander,
|
||||
Boris Schäling,
|
||||
Pim Schellart,
|
||||
Ivan Smirnov, and
|
||||
Patrick Stewart.
|
||||
|
||||
### License
|
||||
|
||||
pybind11 is provided under a BSD-style license that can be found in the
|
||||
``LICENSE`` file. By using, distributing, or contributing to this project,
|
||||
you agree to the terms and conditions of this license.
|
471
third-party/pybind11/include/pybind11/attr.h
vendored
Normal file
471
third-party/pybind11/include/pybind11/attr.h
vendored
Normal file
@ -0,0 +1,471 @@
|
||||
/*
|
||||
pybind11/attr.h: Infrastructure for processing custom
|
||||
type and function attributes
|
||||
|
||||
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 "cast.h"
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
/// \addtogroup annotations
|
||||
/// @{
|
||||
|
||||
/// Annotation for methods
|
||||
struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };
|
||||
|
||||
/// Annotation for operators
|
||||
struct is_operator { };
|
||||
|
||||
/// Annotation for parent scope
|
||||
struct scope { handle value; scope(const handle &s) : value(s) { } };
|
||||
|
||||
/// Annotation for documentation
|
||||
struct doc { const char *value; doc(const char *value) : value(value) { } };
|
||||
|
||||
/// Annotation for function names
|
||||
struct name { const char *value; name(const char *value) : value(value) { } };
|
||||
|
||||
/// Annotation indicating that a function is an overload associated with a given "sibling"
|
||||
struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } };
|
||||
|
||||
/// Annotation indicating that a class derives from another given type
|
||||
template <typename T> struct base {
|
||||
PYBIND11_DEPRECATED("base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
|
||||
base() { }
|
||||
};
|
||||
|
||||
/// Keep patient alive while nurse lives
|
||||
template <size_t Nurse, size_t Patient> struct keep_alive { };
|
||||
|
||||
/// Annotation indicating that a class is involved in a multiple inheritance relationship
|
||||
struct multiple_inheritance { };
|
||||
|
||||
/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class
|
||||
struct dynamic_attr { };
|
||||
|
||||
/// Annotation which enables the buffer protocol for a type
|
||||
struct buffer_protocol { };
|
||||
|
||||
/// Annotation which requests that a special metaclass is created for a type
|
||||
struct metaclass {
|
||||
handle value;
|
||||
|
||||
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
|
||||
metaclass() {}
|
||||
|
||||
/// Override pybind11's default metaclass
|
||||
explicit metaclass(handle value) : value(value) { }
|
||||
};
|
||||
|
||||
/// Annotation to mark enums as an arithmetic type
|
||||
struct arithmetic { };
|
||||
|
||||
/** \rst
|
||||
A call policy which places one or more guard variables (``Ts...``) around the function call.
|
||||
|
||||
For example, this definition:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
m.def("foo", foo, py::call_guard<T>());
|
||||
|
||||
is equivalent to the following pseudocode:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
m.def("foo", [](args...) {
|
||||
T scope_guard;
|
||||
return foo(args...); // forwarded arguments
|
||||
});
|
||||
\endrst */
|
||||
template <typename... Ts> struct call_guard;
|
||||
|
||||
template <> struct call_guard<> { using type = detail::void_type; };
|
||||
|
||||
template <typename T>
|
||||
struct call_guard<T> {
|
||||
static_assert(std::is_default_constructible<T>::value,
|
||||
"The guard type must be default constructible");
|
||||
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
struct call_guard<T, Ts...> {
|
||||
struct type {
|
||||
T guard{}; // Compose multiple guard types with left-to-right default-constructor order
|
||||
typename call_guard<Ts...>::type next{};
|
||||
};
|
||||
};
|
||||
|
||||
/// @} annotations
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
/* Forward declarations */
|
||||
enum op_id : int;
|
||||
enum op_type : int;
|
||||
struct undefined_t;
|
||||
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
|
||||
template <typename... Args> struct init;
|
||||
template <typename... Args> struct init_alias;
|
||||
inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
|
||||
|
||||
/// Internal data structure which holds metadata about a keyword argument
|
||||
struct argument_record {
|
||||
const char *name; ///< Argument name
|
||||
const char *descr; ///< Human-readable version of the argument value
|
||||
handle value; ///< Associated Python object
|
||||
bool convert : 1; ///< True if the argument is allowed to convert 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)
|
||||
: name(name), descr(descr), value(value), convert(convert), none(none) { }
|
||||
};
|
||||
|
||||
/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.)
|
||||
struct function_record {
|
||||
function_record()
|
||||
: is_constructor(false), is_stateless(false), is_operator(false),
|
||||
has_args(false), has_kwargs(false), is_method(false) { }
|
||||
|
||||
/// Function name
|
||||
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
|
||||
|
||||
// User-specified documentation string
|
||||
char *doc = nullptr;
|
||||
|
||||
/// Human-readable version of the function signature
|
||||
char *signature = nullptr;
|
||||
|
||||
/// List of registered keyword arguments
|
||||
std::vector<argument_record> args;
|
||||
|
||||
/// Pointer to lambda function which converts arguments and performs the actual call
|
||||
handle (*impl) (function_call &) = nullptr;
|
||||
|
||||
/// Storage for the wrapped function pointer and captured data, if any
|
||||
void *data[3] = { };
|
||||
|
||||
/// Pointer to custom destructor for 'data' (if needed)
|
||||
void (*free_data) (function_record *ptr) = nullptr;
|
||||
|
||||
/// Return value policy associated with this function
|
||||
return_value_policy policy = return_value_policy::automatic;
|
||||
|
||||
/// True if name == '__init__'
|
||||
bool is_constructor : 1;
|
||||
|
||||
/// True if this is a stateless function pointer
|
||||
bool is_stateless : 1;
|
||||
|
||||
/// True if this is an operator (__add__), etc.
|
||||
bool is_operator : 1;
|
||||
|
||||
/// True if the function has a '*args' argument
|
||||
bool has_args : 1;
|
||||
|
||||
/// True if the function has a '**kwargs' argument
|
||||
bool has_kwargs : 1;
|
||||
|
||||
/// True if this is a method
|
||||
bool is_method : 1;
|
||||
|
||||
/// Number of arguments (including py::args and/or py::kwargs, if present)
|
||||
std::uint16_t nargs;
|
||||
|
||||
/// Python method object
|
||||
PyMethodDef *def = nullptr;
|
||||
|
||||
/// Python handle to the parent scope (a class or a module)
|
||||
handle scope;
|
||||
|
||||
/// Python handle to the sibling function representing an overload chain
|
||||
handle sibling;
|
||||
|
||||
/// Pointer to next overload
|
||||
function_record *next = nullptr;
|
||||
};
|
||||
|
||||
/// Special data structure which (temporarily) holds metadata about a bound class
|
||||
struct type_record {
|
||||
PYBIND11_NOINLINE type_record()
|
||||
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false) { }
|
||||
|
||||
/// Handle to the parent scope
|
||||
handle scope;
|
||||
|
||||
/// Name of the class
|
||||
const char *name = nullptr;
|
||||
|
||||
// Pointer to RTTI type_info data structure
|
||||
const std::type_info *type = nullptr;
|
||||
|
||||
/// How large is the underlying C++ type?
|
||||
size_t type_size = 0;
|
||||
|
||||
/// How large is the type's holder?
|
||||
size_t holder_size = 0;
|
||||
|
||||
/// The global operator new can be overridden with a class-specific variant
|
||||
void *(*operator_new)(size_t) = ::operator new;
|
||||
|
||||
/// Function pointer to class_<..>::init_holder
|
||||
void (*init_holder)(instance *, const void *) = nullptr;
|
||||
|
||||
/// Function pointer to class_<..>::dealloc
|
||||
void (*dealloc)(const detail::value_and_holder &) = nullptr;
|
||||
|
||||
/// List of base classes of the newly created type
|
||||
list bases;
|
||||
|
||||
/// Optional docstring
|
||||
const char *doc = nullptr;
|
||||
|
||||
/// Custom metaclass (optional)
|
||||
handle metaclass;
|
||||
|
||||
/// Multiple inheritance marker
|
||||
bool multiple_inheritance : 1;
|
||||
|
||||
/// Does the class manage a __dict__?
|
||||
bool dynamic_attr : 1;
|
||||
|
||||
/// Does the class implement the buffer protocol?
|
||||
bool buffer_protocol : 1;
|
||||
|
||||
/// Is the default (unique_ptr) holder type used?
|
||||
bool default_holder : 1;
|
||||
|
||||
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) {
|
||||
auto base_info = detail::get_type_info(base, false);
|
||||
if (!base_info) {
|
||||
std::string tname(base.name());
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("generic_type: type \"" + std::string(name) +
|
||||
"\" referenced unknown base type \"" + tname + "\"");
|
||||
}
|
||||
|
||||
if (default_holder != base_info->default_holder) {
|
||||
std::string tname(base.name());
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("generic_type: type \"" + std::string(name) + "\" " +
|
||||
(default_holder ? "does not have" : "has") +
|
||||
" a non-default holder type while its base \"" + tname + "\" " +
|
||||
(base_info->default_holder ? "does not" : "does"));
|
||||
}
|
||||
|
||||
bases.append((PyObject *) base_info->type);
|
||||
|
||||
if (base_info->type->tp_dictoffset != 0)
|
||||
dynamic_attr = true;
|
||||
|
||||
if (caster)
|
||||
base_info->implicit_casts.emplace_back(type, caster);
|
||||
}
|
||||
};
|
||||
|
||||
inline function_call::function_call(function_record &f, handle p) :
|
||||
func(f), parent(p) {
|
||||
args.reserve(f.nargs);
|
||||
args_convert.reserve(f.nargs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Partial template specializations to process custom attributes provided to
|
||||
* cpp_function_ and class_. These are either used to initialize the respective
|
||||
* fields in the type_record and function_record data structures or executed at
|
||||
* runtime to deal with custom call policies (e.g. keep_alive).
|
||||
*/
|
||||
template <typename T, typename SFINAE = void> struct process_attribute;
|
||||
|
||||
template <typename T> struct process_attribute_default {
|
||||
/// Default implementation: do nothing
|
||||
static void init(const T &, function_record *) { }
|
||||
static void init(const T &, type_record *) { }
|
||||
static void precall(function_call &) { }
|
||||
static void postcall(function_call &, handle) { }
|
||||
};
|
||||
|
||||
/// Process an attribute specifying the function's name
|
||||
template <> struct process_attribute<name> : process_attribute_default<name> {
|
||||
static void init(const name &n, function_record *r) { r->name = const_cast<char *>(n.value); }
|
||||
};
|
||||
|
||||
/// Process an attribute specifying the function's docstring
|
||||
template <> struct process_attribute<doc> : process_attribute_default<doc> {
|
||||
static void init(const doc &n, function_record *r) { r->doc = const_cast<char *>(n.value); }
|
||||
};
|
||||
|
||||
/// Process an attribute specifying the function's docstring (provided as a C-style string)
|
||||
template <> struct process_attribute<const char *> : process_attribute_default<const char *> {
|
||||
static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); }
|
||||
static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); }
|
||||
};
|
||||
template <> struct process_attribute<char *> : process_attribute<const char *> { };
|
||||
|
||||
/// Process an attribute indicating the function's return value policy
|
||||
template <> struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> {
|
||||
static void init(const return_value_policy &p, function_record *r) { r->policy = p; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates that this is an overloaded function associated with a given sibling
|
||||
template <> struct process_attribute<sibling> : process_attribute_default<sibling> {
|
||||
static void init(const sibling &s, function_record *r) { r->sibling = s.value; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates that this function is a method
|
||||
template <> struct process_attribute<is_method> : process_attribute_default<is_method> {
|
||||
static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates the parent scope of a method
|
||||
template <> struct process_attribute<scope> : process_attribute_default<scope> {
|
||||
static void init(const scope &s, function_record *r) { r->scope = s.value; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates that this function is an operator
|
||||
template <> struct process_attribute<is_operator> : process_attribute_default<is_operator> {
|
||||
static void init(const is_operator &, function_record *r) { r->is_operator = true; }
|
||||
};
|
||||
|
||||
/// Process a keyword argument attribute (*without* a default value)
|
||||
template <> struct process_attribute<arg> : process_attribute_default<arg> {
|
||||
static void init(const arg &a, function_record *r) {
|
||||
if (r->is_method && r->args.empty())
|
||||
r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/);
|
||||
r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none);
|
||||
}
|
||||
};
|
||||
|
||||
/// Process a keyword argument attribute (*with* a default value)
|
||||
template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
||||
static void init(const arg_v &a, function_record *r) {
|
||||
if (r->is_method && r->args.empty())
|
||||
r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/);
|
||||
|
||||
if (!a.value) {
|
||||
#if !defined(NDEBUG)
|
||||
std::string descr("'");
|
||||
if (a.name) descr += std::string(a.name) + ": ";
|
||||
descr += a.type + "'";
|
||||
if (r->is_method) {
|
||||
if (r->name)
|
||||
descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'";
|
||||
else
|
||||
descr += " in method of '" + (std::string) str(r->scope) + "'";
|
||||
} else if (r->name) {
|
||||
descr += " in function '" + (std::string) r->name + "'";
|
||||
}
|
||||
pybind11_fail("arg(): could not convert default argument "
|
||||
+ descr + " into a Python object (type not registered yet?)");
|
||||
#else
|
||||
pybind11_fail("arg(): could not convert default argument "
|
||||
"into a Python object (type not registered yet?). "
|
||||
"Compile in debug mode for more information.");
|
||||
#endif
|
||||
}
|
||||
r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none);
|
||||
}
|
||||
};
|
||||
|
||||
/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that)
|
||||
template <typename T>
|
||||
struct process_attribute<T, enable_if_t<is_pyobject<T>::value>> : process_attribute_default<handle> {
|
||||
static void init(const handle &h, type_record *r) { r->bases.append(h); }
|
||||
};
|
||||
|
||||
/// Process a parent class attribute (deprecated, does not support multiple inheritance)
|
||||
template <typename T>
|
||||
struct process_attribute<base<T>> : process_attribute_default<base<T>> {
|
||||
static void init(const base<T> &, type_record *r) { r->add_base(typeid(T), nullptr); }
|
||||
};
|
||||
|
||||
/// Process a multiple inheritance attribute
|
||||
template <>
|
||||
struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> {
|
||||
static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr> {
|
||||
static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct process_attribute<buffer_protocol> : process_attribute_default<buffer_protocol> {
|
||||
static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct process_attribute<metaclass> : process_attribute_default<metaclass> {
|
||||
static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; }
|
||||
};
|
||||
|
||||
|
||||
/// Process an 'arithmetic' attribute for enums (does nothing here)
|
||||
template <>
|
||||
struct process_attribute<arithmetic> : process_attribute_default<arithmetic> {};
|
||||
|
||||
template <typename... 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
|
||||
* pre-call handler if both Nurse, Patient != 0 and use the post-call handler
|
||||
* otherwise
|
||||
*/
|
||||
template <size_t Nurse, size_t Patient> struct process_attribute<keep_alive<Nurse, Patient>> : public process_attribute_default<keep_alive<Nurse, Patient>> {
|
||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
|
||||
static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); }
|
||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
|
||||
static void postcall(function_call &, handle) { }
|
||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
|
||||
static void precall(function_call &) { }
|
||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
|
||||
static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); }
|
||||
};
|
||||
|
||||
/// Recursively iterate over variadic template arguments
|
||||
template <typename... Args> struct process_attributes {
|
||||
static void init(const Args&... args, function_record *r) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
|
||||
ignore_unused(unused);
|
||||
}
|
||||
static void init(const Args&... args, type_record *r) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
|
||||
ignore_unused(unused);
|
||||
}
|
||||
static void precall(function_call &call) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::precall(call), 0) ... };
|
||||
ignore_unused(unused);
|
||||
}
|
||||
static void postcall(function_call &call, handle fn_ret) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0) ... };
|
||||
ignore_unused(unused);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using is_call_guard = is_instantiation<call_guard, T>;
|
||||
|
||||
/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found)
|
||||
template <typename... Extra>
|
||||
using extract_guard_t = typename exactly_one_t<is_call_guard, call_guard<>, Extra...>::type;
|
||||
|
||||
/// Check the number of named arguments at compile time
|
||||
template <typename... Extra,
|
||||
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
|
||||
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
|
||||
constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
|
||||
return named == 0 || (self + named + has_args + has_kwargs) == nargs;
|
||||
}
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(pybind11)
|
108
third-party/pybind11/include/pybind11/buffer_info.h
vendored
Normal file
108
third-party/pybind11/include/pybind11/buffer_info.h
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
pybind11/buffer_info.h: Python buffer object interface
|
||||
|
||||
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 "common.h"
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
/// Information record describing a Python buffer object
|
||||
struct buffer_info {
|
||||
void *ptr = nullptr; // Pointer to the underlying storage
|
||||
ssize_t itemsize = 0; // Size of individual items in bytes
|
||||
ssize_t size = 0; // Total number of entries
|
||||
std::string format; // For homogeneous buffers, this should be set to format_descriptor<T>::format()
|
||||
ssize_t ndim = 0; // Number of dimensions
|
||||
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
|
||||
std::vector<ssize_t> strides; // Number of entries between adjacent entries (for each per dimension)
|
||||
|
||||
buffer_info() { }
|
||||
|
||||
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
|
||||
detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in)
|
||||
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
|
||||
shape(std::move(shape_in)), strides(std::move(strides_in)) {
|
||||
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size())
|
||||
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
|
||||
for (size_t i = 0; i < (size_t) ndim; ++i)
|
||||
size *= shape[i];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_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)) { }
|
||||
|
||||
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size)
|
||||
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { }
|
||||
|
||||
template <typename T>
|
||||
buffer_info(T *ptr, ssize_t size)
|
||||
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size) { }
|
||||
|
||||
explicit buffer_info(Py_buffer *view, bool ownview = true)
|
||||
: buffer_info(view->buf, view->itemsize, view->format, view->ndim,
|
||||
{view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) {
|
||||
this->view = view;
|
||||
this->ownview = ownview;
|
||||
}
|
||||
|
||||
buffer_info(const buffer_info &) = delete;
|
||||
buffer_info& operator=(const buffer_info &) = delete;
|
||||
|
||||
buffer_info(buffer_info &&other) {
|
||||
(*this) = std::move(other);
|
||||
}
|
||||
|
||||
buffer_info& operator=(buffer_info &&rhs) {
|
||||
ptr = rhs.ptr;
|
||||
itemsize = rhs.itemsize;
|
||||
size = rhs.size;
|
||||
format = std::move(rhs.format);
|
||||
ndim = rhs.ndim;
|
||||
shape = std::move(rhs.shape);
|
||||
strides = std::move(rhs.strides);
|
||||
std::swap(view, rhs.view);
|
||||
std::swap(ownview, rhs.ownview);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~buffer_info() {
|
||||
if (view && ownview) { PyBuffer_Release(view); delete view; }
|
||||
}
|
||||
|
||||
private:
|
||||
struct private_ctr_tag { };
|
||||
|
||||
buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
|
||||
detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in)
|
||||
: buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { }
|
||||
|
||||
Py_buffer *view = nullptr;
|
||||
bool ownview = false;
|
||||
};
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T, typename SFINAE = void> struct compare_buffer_info {
|
||||
static bool compare(const buffer_info& b) {
|
||||
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
|
||||
static bool compare(const buffer_info& b) {
|
||||
return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor<T>::value ||
|
||||
((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned<T>::value ? "L" : "l")) ||
|
||||
((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned<T>::value ? "N" : "n")));
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(pybind11)
|
2015
third-party/pybind11/include/pybind11/cast.h
vendored
Normal file
2015
third-party/pybind11/include/pybind11/cast.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
162
third-party/pybind11/include/pybind11/chrono.h
vendored
Normal file
162
third-party/pybind11/include/pybind11/chrono.h
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime
|
||||
|
||||
Copyright (c) 2016 Trent Houliston <trent@houliston.me> and
|
||||
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 "pybind11.h"
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <datetime.h>
|
||||
|
||||
// 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
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename type> class duration_caster {
|
||||
public:
|
||||
typedef typename type::rep rep;
|
||||
typedef typename type::period period;
|
||||
|
||||
typedef std::chrono::duration<uint_fast32_t, std::ratio<86400>> days;
|
||||
|
||||
bool load(handle src, bool) {
|
||||
using namespace std::chrono;
|
||||
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
|
||||
if (!src) return false;
|
||||
// If invoked with datetime.delta object
|
||||
if (PyDelta_Check(src.ptr())) {
|
||||
value = type(duration_cast<duration<rep, period>>(
|
||||
days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
|
||||
+ seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
|
||||
+ microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
|
||||
return true;
|
||||
}
|
||||
// If invoked with a float we assume it is seconds and convert
|
||||
else if (PyFloat_Check(src.ptr())) {
|
||||
value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr()))));
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
// If this is a duration just return it back
|
||||
static const std::chrono::duration<rep, period>& get_duration(const std::chrono::duration<rep, period> &src) {
|
||||
return src;
|
||||
}
|
||||
|
||||
// If this is a time_point get the time_since_epoch
|
||||
template <typename Clock> static std::chrono::duration<rep, period> get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) {
|
||||
return src.time_since_epoch();
|
||||
}
|
||||
|
||||
static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
using namespace std::chrono;
|
||||
|
||||
// Use overloaded function to get our duration from our source
|
||||
// Works out if it is a duration or time_point and get the duration
|
||||
auto d = get_duration(src);
|
||||
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
|
||||
// Declare these special duration types so the conversions happen with the correct primitive types (int)
|
||||
using dd_t = duration<int, std::ratio<86400>>;
|
||||
using ss_t = duration<int, std::ratio<1>>;
|
||||
using us_t = duration<int, std::micro>;
|
||||
|
||||
auto dd = duration_cast<dd_t>(d);
|
||||
auto subd = d - dd;
|
||||
auto ss = duration_cast<ss_t>(subd);
|
||||
auto us = duration_cast<us_t>(subd - ss);
|
||||
return PyDelta_FromDSU(dd.count(), ss.count(), us.count());
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(type, _("datetime.timedelta"));
|
||||
};
|
||||
|
||||
// This is for casting times on the system clock into datetime.datetime instances
|
||||
template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
||||
public:
|
||||
typedef std::chrono::time_point<std::chrono::system_clock, Duration> type;
|
||||
bool load(handle src, bool) {
|
||||
using namespace std::chrono;
|
||||
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
|
||||
if (!src) return false;
|
||||
if (PyDateTime_Check(src.ptr())) {
|
||||
std::tm cal;
|
||||
cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
|
||||
cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
|
||||
cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
|
||||
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
||||
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
||||
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
||||
cal.tm_isdst = -1;
|
||||
|
||||
value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
using namespace std::chrono;
|
||||
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
|
||||
std::time_t tt = system_clock::to_time_t(src);
|
||||
// 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);
|
||||
|
||||
// Declare these special duration types so the conversions happen with the correct primitive types (int)
|
||||
using us_t = duration<int, std::micro>;
|
||||
|
||||
return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
|
||||
localtime.tm_mon + 1,
|
||||
localtime.tm_mday,
|
||||
localtime.tm_hour,
|
||||
localtime.tm_min,
|
||||
localtime.tm_sec,
|
||||
(duration_cast<us_t>(src.time_since_epoch() % seconds(1))).count());
|
||||
}
|
||||
PYBIND11_TYPE_CASTER(type, _("datetime.datetime"));
|
||||
};
|
||||
|
||||
// Other clocks that are not the system clock are not measured as datetime.datetime objects
|
||||
// since they are not measured on calendar time. So instead we just make them timedeltas
|
||||
// Or if they have passed us a time as a float we convert that
|
||||
template <typename Clock, typename Duration> class type_caster<std::chrono::time_point<Clock, Duration>>
|
||||
: public duration_caster<std::chrono::time_point<Clock, Duration>> {
|
||||
};
|
||||
|
||||
template <typename Rep, typename Period> class type_caster<std::chrono::duration<Rep, Period>>
|
||||
: public duration_caster<std::chrono::duration<Rep, Period>> {
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(pybind11)
|
602
third-party/pybind11/include/pybind11/class_support.h
vendored
Normal file
602
third-party/pybind11/include/pybind11/class_support.h
vendored
Normal file
@ -0,0 +1,602 @@
|
||||
/*
|
||||
pybind11/class_support.h: Python C API implementation details for py::class_
|
||||
|
||||
Copyright (c) 2017 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 "attr.h"
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
inline PyTypeObject *type_incref(PyTypeObject *type) {
|
||||
Py_INCREF(type);
|
||||
return type;
|
||||
}
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
|
||||
/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance.
|
||||
extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) {
|
||||
return PyProperty_Type.tp_descr_get(self, cls, cls);
|
||||
}
|
||||
|
||||
/// `pybind11_static_property.__set__()`: Just like the above `__get__()`.
|
||||
extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) {
|
||||
PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj);
|
||||
return PyProperty_Type.tp_descr_set(self, cls, value);
|
||||
}
|
||||
|
||||
/** 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.
|
||||
Return value: New reference. */
|
||||
inline PyTypeObject *make_static_property_type() {
|
||||
constexpr auto *name = "pybind11_static_property";
|
||||
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
|
||||
|
||||
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||
issue no Python C API calls which could potentially invoke the
|
||||
garbage collector (the GC will call type_traverse(), which will in
|
||||
turn find the newly constructed type in an invalid state) */
|
||||
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
|
||||
if (!heap_type)
|
||||
pybind11_fail("make_static_property_type(): error allocating type!");
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
auto type = &heap_type->ht_type;
|
||||
type->tp_name = name;
|
||||
type->tp_base = type_incref(&PyProperty_Type);
|
||||
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||
type->tp_descr_get = pybind11_static_get;
|
||||
type->tp_descr_set = pybind11_static_set;
|
||||
|
||||
if (PyType_Ready(type) < 0)
|
||||
pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
#else // PYPY
|
||||
|
||||
/** PyPy has some issues with the above C API, so we evaluate Python code instead.
|
||||
This function will only be called once so performance isn't really a concern.
|
||||
Return value: New reference. */
|
||||
inline PyTypeObject *make_static_property_type() {
|
||||
auto d = dict();
|
||||
PyObject *result = PyRun_String(R"(\
|
||||
class pybind11_static_property(property):
|
||||
def __get__(self, obj, cls):
|
||||
return property.__get__(self, cls, cls)
|
||||
|
||||
def __set__(self, obj, value):
|
||||
cls = obj if isinstance(obj, type) else type(obj)
|
||||
property.__set__(self, cls, value)
|
||||
)", Py_file_input, d.ptr(), d.ptr()
|
||||
);
|
||||
if (result == nullptr)
|
||||
throw error_already_set();
|
||||
Py_DECREF(result);
|
||||
return (PyTypeObject *) d["pybind11_static_property"].cast<object>().release().ptr();
|
||||
}
|
||||
|
||||
#endif // PYPY
|
||||
|
||||
/** Types with static properties need to handle `Type.static_prop = x` in a specific way.
|
||||
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
|
||||
the underlying C++ data structure. */
|
||||
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
|
||||
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
|
||||
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
|
||||
|
||||
// The following assignment combinations are possible:
|
||||
// 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)`
|
||||
// 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop`
|
||||
// 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment
|
||||
const auto static_prop = (PyObject *) get_internals().static_property_type;
|
||||
const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop)
|
||||
&& !PyObject_IsInstance(value, static_prop);
|
||||
if (call_descr_set) {
|
||||
// Call `static_property.__set__()` instead of replacing the `static_property`.
|
||||
#if !defined(PYPY_VERSION)
|
||||
return Py_TYPE(descr)->tp_descr_set(descr, obj, value);
|
||||
#else
|
||||
if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) {
|
||||
Py_DECREF(result);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// Replace existing attribute.
|
||||
return PyType_Type.tp_setattro(obj, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
/**
|
||||
* 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,
|
||||
* when called on a class, or a PyMethod, when called on an instance. Override that behaviour here
|
||||
* to do a special case bypass for PyInstanceMethod_Types.
|
||||
*/
|
||||
extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) {
|
||||
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
|
||||
if (descr && PyInstanceMethod_Check(descr)) {
|
||||
Py_INCREF(descr);
|
||||
return descr;
|
||||
}
|
||||
else {
|
||||
return PyType_Type.tp_getattro(obj, name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/** 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`.
|
||||
Return value: New reference. */
|
||||
inline PyTypeObject* make_default_metaclass() {
|
||||
constexpr auto *name = "pybind11_type";
|
||||
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
|
||||
|
||||
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||
issue no Python C API calls which could potentially invoke the
|
||||
garbage collector (the GC will call type_traverse(), which will in
|
||||
turn find the newly constructed type in an invalid state) */
|
||||
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
|
||||
if (!heap_type)
|
||||
pybind11_fail("make_default_metaclass(): error allocating metaclass!");
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
auto type = &heap_type->ht_type;
|
||||
type->tp_name = name;
|
||||
type->tp_base = type_incref(&PyType_Type);
|
||||
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||
|
||||
type->tp_setattro = pybind11_meta_setattro;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
type->tp_getattro = pybind11_meta_getattro;
|
||||
#endif
|
||||
|
||||
if (PyType_Ready(type) < 0)
|
||||
pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!");
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/// For multiple inheritance types we need to recursively register/deregister base pointers for any
|
||||
/// base classes with pointers that are difference from the instance value pointer so that we can
|
||||
/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs.
|
||||
inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self,
|
||||
bool (*f)(void * /*parentptr*/, instance * /*self*/)) {
|
||||
for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) {
|
||||
if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) {
|
||||
for (auto &c : parent_tinfo->implicit_casts) {
|
||||
if (c.first == tinfo->cpptype) {
|
||||
auto *parentptr = c.second(valueptr);
|
||||
if (parentptr != valueptr)
|
||||
f(parentptr, self);
|
||||
traverse_offset_bases(parentptr, parent_tinfo, self, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool register_instance_impl(void *ptr, instance *self) {
|
||||
get_internals().registered_instances.emplace(ptr, self);
|
||||
return true; // unused, but gives the same signature as the deregister func
|
||||
}
|
||||
inline bool deregister_instance_impl(void *ptr, instance *self) {
|
||||
auto ®istered_instances = get_internals().registered_instances;
|
||||
auto range = registered_instances.equal_range(ptr);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
if (Py_TYPE(self) == Py_TYPE(it->second)) {
|
||||
registered_instances.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void register_instance(instance *self, void *valptr, const type_info *tinfo) {
|
||||
register_instance_impl(valptr, self);
|
||||
if (!tinfo->simple_ancestors)
|
||||
traverse_offset_bases(valptr, tinfo, self, register_instance_impl);
|
||||
}
|
||||
|
||||
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) {
|
||||
bool ret = deregister_instance_impl(valptr, self);
|
||||
if (!tinfo->simple_ancestors)
|
||||
traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Instance creation function for all pybind11 types. It only allocates space for the C++ object
|
||||
/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't
|
||||
/// call the constructor -- an `__init__` function must do that. If allocating value, the instance
|
||||
/// is registered; otherwise register_instance will need to be called once the value has been
|
||||
/// assigned.
|
||||
inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) {
|
||||
#if defined(PYPY_VERSION)
|
||||
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
|
||||
// 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));
|
||||
if (type->tp_basicsize < instance_size) {
|
||||
type->tp_basicsize = instance_size;
|
||||
}
|
||||
#endif
|
||||
PyObject *self = type->tp_alloc(type, 0);
|
||||
auto inst = reinterpret_cast<instance *>(self);
|
||||
// Allocate the value/holder internals:
|
||||
inst->allocate_layout();
|
||||
|
||||
inst->owned = true;
|
||||
// Allocate (if requested) the value pointers; otherwise leave them as nullptr
|
||||
if (allocate_value) {
|
||||
for (auto &v_h : values_and_holders(inst)) {
|
||||
void *&vptr = v_h.value_ptr();
|
||||
vptr = v_h.type->operator_new(v_h.type->type_size);
|
||||
register_instance(inst, vptr, v_h.type);
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Instance creation function for all pybind11 types. It only allocates space for the
|
||||
/// C++ object, but doesn't call the constructor -- an `__init__` function must do that.
|
||||
extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) {
|
||||
return make_new_instance(type);
|
||||
}
|
||||
|
||||
/// An `__init__` function constructs the C++ object. Users should provide at least one
|
||||
/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the
|
||||
/// following default function will be used which simply throws an exception.
|
||||
extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) {
|
||||
PyTypeObject *type = Py_TYPE(self);
|
||||
std::string msg;
|
||||
#if defined(PYPY_VERSION)
|
||||
msg += handle((PyObject *) type).attr("__module__").cast<std::string>() + ".";
|
||||
#endif
|
||||
msg += type->tp_name;
|
||||
msg += ": No constructor defined!";
|
||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline void add_patient(PyObject *nurse, PyObject *patient) {
|
||||
auto &internals = get_internals();
|
||||
auto instance = reinterpret_cast<detail::instance *>(nurse);
|
||||
instance->has_patients = true;
|
||||
Py_INCREF(patient);
|
||||
internals.patients[nurse].push_back(patient);
|
||||
}
|
||||
|
||||
inline void clear_patients(PyObject *self) {
|
||||
auto instance = reinterpret_cast<detail::instance *>(self);
|
||||
auto &internals = get_internals();
|
||||
auto pos = internals.patients.find(self);
|
||||
assert(pos != internals.patients.end());
|
||||
// Clearing the patients can cause more Python code to run, which
|
||||
// can invalidate the iterator. Extract the vector of patients
|
||||
// from the unordered_map first.
|
||||
auto patients = std::move(pos->second);
|
||||
internals.patients.erase(pos);
|
||||
instance->has_patients = false;
|
||||
for (PyObject *&patient : patients)
|
||||
Py_CLEAR(patient);
|
||||
}
|
||||
|
||||
/// Clears all internal data from the instance and removes it from registered instances in
|
||||
/// preparation for deallocation.
|
||||
inline void clear_instance(PyObject *self) {
|
||||
auto instance = reinterpret_cast<detail::instance *>(self);
|
||||
|
||||
// Deallocate any values/holders, if present:
|
||||
for (auto &v_h : values_and_holders(instance)) {
|
||||
if (v_h) {
|
||||
if (instance->owned || v_h.holder_constructed())
|
||||
v_h.type->dealloc(v_h);
|
||||
|
||||
if (!deregister_instance(instance, v_h.value_ptr(), v_h.type))
|
||||
pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
|
||||
}
|
||||
}
|
||||
// Deallocate the value/holder layout internals:
|
||||
instance->deallocate_layout();
|
||||
|
||||
if (instance->weakrefs)
|
||||
PyObject_ClearWeakRefs(self);
|
||||
|
||||
PyObject **dict_ptr = _PyObject_GetDictPtr(self);
|
||||
if (dict_ptr)
|
||||
Py_CLEAR(*dict_ptr);
|
||||
|
||||
if (instance->has_patients)
|
||||
clear_patients(self);
|
||||
}
|
||||
|
||||
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
|
||||
/// to destroy the C++ object itself, while the rest is Python bookkeeping.
|
||||
extern "C" inline void pybind11_object_dealloc(PyObject *self) {
|
||||
clear_instance(self);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
/** 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.
|
||||
Return value: New reference. */
|
||||
inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
||||
constexpr auto *name = "pybind11_object";
|
||||
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
|
||||
|
||||
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||
issue no Python C API calls which could potentially invoke the
|
||||
garbage collector (the GC will call type_traverse(), which will in
|
||||
turn find the newly constructed type in an invalid state) */
|
||||
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
||||
if (!heap_type)
|
||||
pybind11_fail("make_object_base_type(): error allocating type!");
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
|
||||
auto type = &heap_type->ht_type;
|
||||
type->tp_name = name;
|
||||
type->tp_base = type_incref(&PyBaseObject_Type);
|
||||
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
|
||||
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||
|
||||
type->tp_new = pybind11_object_new;
|
||||
type->tp_init = pybind11_object_init;
|
||||
type->tp_dealloc = pybind11_object_dealloc;
|
||||
|
||||
/* Support weak references (needed for the keep_alive feature) */
|
||||
type->tp_weaklistoffset = offsetof(instance, weakrefs);
|
||||
|
||||
if (PyType_Ready(type) < 0)
|
||||
pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
|
||||
assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
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'",
|
||||
Py_TYPE(new_dict)->tp_name);
|
||||
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__`.
|
||||
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
Py_VISIT(dict);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// dynamic_attr: Allow the GC to clear the dictionary.
|
||||
extern "C" inline int pybind11_clear(PyObject *self) {
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
Py_CLEAR(dict);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Give instances of this type a `__dict__` and opt into garbage collection.
|
||||
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
|
||||
auto type = &heap_type->ht_type;
|
||||
#if defined(PYPY_VERSION)
|
||||
pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are "
|
||||
"currently not supported in "
|
||||
"conjunction with PyPy!");
|
||||
#endif
|
||||
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
|
||||
type->tp_dictoffset = type->tp_basicsize; // place dict at the end
|
||||
type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it
|
||||
type->tp_traverse = pybind11_traverse;
|
||||
type->tp_clear = pybind11_clear;
|
||||
|
||||
static PyGetSetDef getset[] = {
|
||||
{const_cast<char*>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr}
|
||||
};
|
||||
type->tp_getset = getset;
|
||||
}
|
||||
|
||||
/// buffer_protocol: Fill in the view as specified by flags.
|
||||
extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) {
|
||||
// Look for a `get_buffer` implementation in this type's info or any bases (following MRO).
|
||||
type_info *tinfo = nullptr;
|
||||
for (auto type : reinterpret_borrow<tuple>(Py_TYPE(obj)->tp_mro)) {
|
||||
tinfo = get_type_info((PyTypeObject *) type.ptr());
|
||||
if (tinfo && tinfo->get_buffer)
|
||||
break;
|
||||
}
|
||||
if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) {
|
||||
if (view)
|
||||
view->obj = nullptr;
|
||||
PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
|
||||
return -1;
|
||||
}
|
||||
std::memset(view, 0, sizeof(Py_buffer));
|
||||
buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
|
||||
view->obj = obj;
|
||||
view->ndim = 1;
|
||||
view->internal = info;
|
||||
view->buf = info->ptr;
|
||||
view->itemsize = info->itemsize;
|
||||
view->len = view->itemsize;
|
||||
for (auto s : info->shape)
|
||||
view->len *= s;
|
||||
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
|
||||
view->format = const_cast<char *>(info->format.c_str());
|
||||
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
|
||||
view->ndim = (int) info->ndim;
|
||||
view->strides = &info->strides[0];
|
||||
view->shape = &info->shape[0];
|
||||
}
|
||||
Py_INCREF(view->obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// buffer_protocol: Release the resources of the buffer.
|
||||
extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) {
|
||||
delete (buffer_info *) view->internal;
|
||||
}
|
||||
|
||||
/// Give this type a buffer interface.
|
||||
inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
|
||||
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_releasebuffer = pybind11_releasebuffer;
|
||||
}
|
||||
|
||||
/** Create a brand new Python type according to the `type_record` specification.
|
||||
Return value: New reference. */
|
||||
inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name));
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
auto ht_qualname = name;
|
||||
if (rec.scope && hasattr(rec.scope, "__qualname__")) {
|
||||
ht_qualname = reinterpret_steal<object>(
|
||||
PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr()));
|
||||
}
|
||||
#endif
|
||||
|
||||
object module;
|
||||
if (rec.scope) {
|
||||
if (hasattr(rec.scope, "__module__"))
|
||||
module = rec.scope.attr("__module__");
|
||||
else if (hasattr(rec.scope, "__name__"))
|
||||
module = rec.scope.attr("__name__");
|
||||
}
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
const auto full_name = module ? str(module).cast<std::string>() + "." + rec.name
|
||||
: std::string(rec.name);
|
||||
#else
|
||||
const auto full_name = std::string(rec.name);
|
||||
#endif
|
||||
|
||||
char *tp_doc = nullptr;
|
||||
if (rec.doc && options::show_user_defined_docstrings()) {
|
||||
/* Allocate memory for docstring (using PyObject_MALLOC, since
|
||||
Python will free this later on) */
|
||||
size_t size = strlen(rec.doc) + 1;
|
||||
tp_doc = (char *) PyObject_MALLOC(size);
|
||||
memcpy((void *) tp_doc, rec.doc, size);
|
||||
}
|
||||
|
||||
auto &internals = get_internals();
|
||||
auto bases = tuple(rec.bases);
|
||||
auto base = (bases.size() == 0) ? internals.instance_base
|
||||
: bases[0].ptr();
|
||||
|
||||
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||
issue no Python C API calls which could potentially invoke the
|
||||
garbage collector (the GC will call type_traverse(), which will in
|
||||
turn find the newly constructed type in an invalid state) */
|
||||
auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr()
|
||||
: internals.default_metaclass;
|
||||
|
||||
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
||||
if (!heap_type)
|
||||
pybind11_fail(std::string(rec.name) + ": Unable to create type object!");
|
||||
|
||||
heap_type->ht_name = name.release().ptr();
|
||||
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
|
||||
heap_type->ht_qualname = ht_qualname.release().ptr();
|
||||
#endif
|
||||
|
||||
auto type = &heap_type->ht_type;
|
||||
type->tp_name = strdup(full_name.c_str());
|
||||
type->tp_doc = tp_doc;
|
||||
type->tp_base = type_incref((PyTypeObject *)base);
|
||||
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
|
||||
if (bases.size() > 0)
|
||||
type->tp_bases = bases.release().ptr();
|
||||
|
||||
/* Don't inherit base __init__ */
|
||||
type->tp_init = pybind11_object_init;
|
||||
|
||||
/* Supported protocols */
|
||||
type->tp_as_number = &heap_type->as_number;
|
||||
type->tp_as_sequence = &heap_type->as_sequence;
|
||||
type->tp_as_mapping = &heap_type->as_mapping;
|
||||
|
||||
/* Flags */
|
||||
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
|
||||
#endif
|
||||
|
||||
if (rec.dynamic_attr)
|
||||
enable_dynamic_attributes(heap_type);
|
||||
|
||||
if (rec.buffer_protocol)
|
||||
enable_buffer_protocol(heap_type);
|
||||
|
||||
if (PyType_Ready(type) < 0)
|
||||
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");
|
||||
|
||||
assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)
|
||||
: !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
|
||||
/* Register type with the parent scope */
|
||||
if (rec.scope)
|
||||
setattr(rec.scope, rec.name, (PyObject *) type);
|
||||
|
||||
if (module) // Needed by pydoc
|
||||
setattr((PyObject *) type, "__module__", module);
|
||||
|
||||
return (PyObject *) type;
|
||||
}
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(pybind11)
|
876
third-party/pybind11/include/pybind11/common.h
vendored
Normal file
876
third-party/pybind11/include/pybind11/common.h
vendored
Normal file
@ -0,0 +1,876 @@
|
||||
/*
|
||||
pybind11/common.h -- Basic macros
|
||||
|
||||
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
|
||||
|
||||
#if !defined(NAMESPACE_BEGIN)
|
||||
# define NAMESPACE_BEGIN(name) namespace name {
|
||||
#endif
|
||||
#if !defined(NAMESPACE_END)
|
||||
# define NAMESPACE_END(name) }
|
||||
#endif
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||
# if __cplusplus >= 201402L
|
||||
# define PYBIND11_CPP14
|
||||
# if __cplusplus > 201402L /* Temporary: should be updated to >= the final C++17 value once known */
|
||||
# define PYBIND11_CPP17
|
||||
# endif
|
||||
# endif
|
||||
#elif defined(_MSC_VER)
|
||||
// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented)
|
||||
# if _MSVC_LANG >= 201402L
|
||||
# define PYBIND11_CPP14
|
||||
# if _MSVC_LANG > 201402L && _MSC_VER >= 1910
|
||||
# define PYBIND11_CPP17
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Compiler version assertions
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# if __INTEL_COMPILER < 1500
|
||||
# error pybind11 requires Intel C++ compiler v15 or newer
|
||||
# endif
|
||||
#elif defined(__clang__) && !defined(__apple_build_version__)
|
||||
# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3)
|
||||
# error pybind11 requires clang 3.3 or newer
|
||||
# endif
|
||||
#elif defined(__clang__)
|
||||
// Apple changes clang version macros to its Xcode version; the first Xcode release based on
|
||||
// (upstream) clang 3.3 was Xcode 5:
|
||||
# if __clang_major__ < 5
|
||||
# error pybind11 requires Xcode/clang 5.0 or newer
|
||||
# endif
|
||||
#elif defined(__GNUG__)
|
||||
# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
|
||||
# error pybind11 requires gcc 4.8 or newer
|
||||
# endif
|
||||
#elif defined(_MSC_VER)
|
||||
// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features
|
||||
// (e.g. std::negation) added in 2015u3:
|
||||
# if _MSC_FULL_VER < 190024210
|
||||
# error pybind11 requires MSVC 2015 update 3 or newer
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(PYBIND11_EXPORT)
|
||||
# if defined(WIN32) || defined(_WIN32)
|
||||
# define PYBIND11_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define PYBIND11_EXPORT __attribute__ ((visibility("default")))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define PYBIND11_NOINLINE __declspec(noinline)
|
||||
#else
|
||||
# define PYBIND11_NOINLINE __attribute__ ((noinline))
|
||||
#endif
|
||||
|
||||
#if defined(PYBIND11_CPP14)
|
||||
# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]]
|
||||
#else
|
||||
# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason)))
|
||||
#endif
|
||||
|
||||
#define PYBIND11_VERSION_MAJOR 2
|
||||
#define PYBIND11_VERSION_MINOR 2
|
||||
#define PYBIND11_VERSION_PATCH dev0
|
||||
|
||||
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
|
||||
#if defined(_MSC_VER)
|
||||
# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4)
|
||||
# define HAVE_ROUND 1
|
||||
# endif
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4510 4610 4512 4005)
|
||||
# if defined(_DEBUG)
|
||||
# define PYBIND11_DEBUG_MARKER
|
||||
# undef _DEBUG
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
#include <frameobject.h>
|
||||
#include <pythread.h>
|
||||
|
||||
#if defined(_WIN32) && (defined(min) || defined(max))
|
||||
# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows
|
||||
#endif
|
||||
|
||||
#if defined(isalnum)
|
||||
# undef isalnum
|
||||
# undef isalpha
|
||||
# undef islower
|
||||
# undef isspace
|
||||
# undef isupper
|
||||
# undef tolower
|
||||
# undef toupper
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# if defined(PYBIND11_DEBUG_MARKER)
|
||||
# define _DEBUG
|
||||
# undef PYBIND11_DEBUG_MARKER
|
||||
# endif
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <forward_list>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <typeindex>
|
||||
#include <type_traits>
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
|
||||
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
|
||||
#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check
|
||||
#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION
|
||||
#define PYBIND11_BYTES_CHECK PyBytes_Check
|
||||
#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
|
||||
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING PyBytes_AsString
|
||||
#define PYBIND11_BYTES_SIZE PyBytes_Size
|
||||
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
|
||||
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
|
||||
#define PYBIND11_BYTES_NAME "bytes"
|
||||
#define PYBIND11_STRING_NAME "str"
|
||||
#define PYBIND11_SLICE_OBJECT PyObject
|
||||
#define PYBIND11_FROM_STRING PyUnicode_FromString
|
||||
#define PYBIND11_STR_TYPE ::pybind11::str
|
||||
#define PYBIND11_PLUGIN_IMPL(name) \
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
|
||||
#else
|
||||
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_)
|
||||
#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check
|
||||
#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION
|
||||
#define PYBIND11_BYTES_CHECK PyString_Check
|
||||
#define PYBIND11_BYTES_FROM_STRING PyString_FromString
|
||||
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING PyString_AsString
|
||||
#define PYBIND11_BYTES_SIZE PyString_Size
|
||||
#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
|
||||
#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o))
|
||||
#define PYBIND11_BYTES_NAME "str"
|
||||
#define PYBIND11_STRING_NAME "unicode"
|
||||
#define PYBIND11_SLICE_OBJECT PySliceObject
|
||||
#define PYBIND11_FROM_STRING PyString_FromString
|
||||
#define PYBIND11_STR_TYPE ::pybind11::bytes
|
||||
#define PYBIND11_PLUGIN_IMPL(name) \
|
||||
static PyObject *pybind11_init_wrapper(); \
|
||||
extern "C" PYBIND11_EXPORT void init##name() { \
|
||||
(void)pybind11_init_wrapper(); \
|
||||
} \
|
||||
PyObject *pybind11_init_wrapper()
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200
|
||||
extern "C" {
|
||||
struct _Py_atomic_address { void *value; };
|
||||
PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code
|
||||
#define PYBIND11_STRINGIFY(x) #x
|
||||
#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x)
|
||||
#define PYBIND11_INTERNALS_ID "__pybind11_" \
|
||||
PYBIND11_TOSTRING(PYBIND11_VERSION_MAJOR) "_" PYBIND11_TOSTRING(PYBIND11_VERSION_MINOR) "__"
|
||||
|
||||
/** \rst
|
||||
***Deprecated in favor of PYBIND11_MODULE***
|
||||
|
||||
This macro creates the entry point that will be invoked when the Python interpreter
|
||||
imports a plugin library. Please create a `module` in the function body and return
|
||||
the pointer to its underlying Python object at the end.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
PYBIND11_PLUGIN(example) {
|
||||
pybind11::module m("example", "pybind11 example plugin");
|
||||
/// Set up bindings here
|
||||
return m.ptr();
|
||||
}
|
||||
\endrst */
|
||||
#define PYBIND11_PLUGIN(name) \
|
||||
PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \
|
||||
static PyObject *pybind11_init(); \
|
||||
PYBIND11_PLUGIN_IMPL(name) { \
|
||||
int major, minor; \
|
||||
if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \
|
||||
PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \
|
||||
return nullptr; \
|
||||
} else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \
|
||||
PyErr_Format(PyExc_ImportError, \
|
||||
"Python version mismatch: module was compiled for " \
|
||||
"version %i.%i, while the interpreter is running " \
|
||||
"version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \
|
||||
major, minor); \
|
||||
return nullptr; \
|
||||
} \
|
||||
try { \
|
||||
return pybind11_init(); \
|
||||
} catch (pybind11::error_already_set &e) { \
|
||||
e.clear(); \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
} \
|
||||
PyObject *pybind11_init()
|
||||
|
||||
/** \rst
|
||||
This macro creates the entry point that will be invoked when the Python interpreter
|
||||
imports an extension module. The module name is given as the fist argument and it
|
||||
should not be in quotes. The second macro argument defines a variable of type
|
||||
`py::module` which can be used to initialize the module.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
PYBIND11_MODULE(example, m) {
|
||||
m.doc() = "pybind11 example module";
|
||||
|
||||
// Add bindings here
|
||||
m.def("foo", []() {
|
||||
return "Hello, World!";
|
||||
});
|
||||
}
|
||||
\endrst */
|
||||
#define PYBIND11_MODULE(name, variable) \
|
||||
static void pybind11_init_##name(pybind11::module &); \
|
||||
PYBIND11_PLUGIN_IMPL(name) { \
|
||||
int major, minor; \
|
||||
if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \
|
||||
PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \
|
||||
return nullptr; \
|
||||
} else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \
|
||||
PyErr_Format(PyExc_ImportError, \
|
||||
"Python version mismatch: module was compiled for " \
|
||||
"version %i.%i, while the interpreter is running " \
|
||||
"version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \
|
||||
major, minor); \
|
||||
return nullptr; \
|
||||
} \
|
||||
auto m = pybind11::module(#name); \
|
||||
try { \
|
||||
pybind11_init_##name(m); \
|
||||
return m.ptr(); \
|
||||
} catch (pybind11::error_already_set &e) { \
|
||||
e.clear(); \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
} \
|
||||
void pybind11_init_##name(pybind11::module &variable)
|
||||
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
using ssize_t = Py_ssize_t;
|
||||
using size_t = std::size_t;
|
||||
|
||||
/// Approach used to cast a previously unknown C++ instance into a Python object
|
||||
enum class return_value_policy : uint8_t {
|
||||
/** This is the default return value policy, which falls back to the policy
|
||||
return_value_policy::take_ownership when the return value is a pointer.
|
||||
Otherwise, it uses return_value::move or return_value::copy for rvalue
|
||||
and lvalue references, respectively. See below for a description of what
|
||||
all of these different policies do. */
|
||||
automatic = 0,
|
||||
|
||||
/** As above, but use policy return_value_policy::reference when the return
|
||||
value is a pointer. This is the default conversion policy for function
|
||||
arguments when calling Python functions manually from C++ code (i.e. via
|
||||
handle::operator()). You probably won't need to use this. */
|
||||
automatic_reference,
|
||||
|
||||
/** Reference an existing object (i.e. do not create a new copy) and take
|
||||
ownership. Python will call the destructor and delete operator when the
|
||||
object’s reference count reaches zero. Undefined behavior ensues when
|
||||
the C++ side does the same.. */
|
||||
take_ownership,
|
||||
|
||||
/** Create a new copy of the returned object, which will be owned by
|
||||
Python. This policy is comparably safe because the lifetimes of the two
|
||||
instances are decoupled. */
|
||||
copy,
|
||||
|
||||
/** Use std::move to move the return value contents into a new instance
|
||||
that will be owned by Python. This policy is comparably safe because the
|
||||
lifetimes of the two instances (move source and destination) are
|
||||
decoupled. */
|
||||
move,
|
||||
|
||||
/** Reference an existing object, but do not take ownership. The C++ side
|
||||
is responsible for managing the object’s lifetime and deallocating it
|
||||
when it is no longer used. Warning: undefined behavior will ensue when
|
||||
the C++ side deletes an object that is still referenced and used by
|
||||
Python. */
|
||||
reference,
|
||||
|
||||
/** This policy only applies to methods and properties. It references the
|
||||
object without taking ownership similar to the above
|
||||
return_value_policy::reference policy. In contrast to that policy, the
|
||||
function or property’s implicit this argument (called the parent) is
|
||||
considered to be the the owner of the return value (the child).
|
||||
pybind11 then couples the lifetime of the parent to the child via a
|
||||
reference relationship that ensures that the parent cannot be garbage
|
||||
collected while Python is still using the child. More advanced
|
||||
variations of this scheme are also possible using combinations of
|
||||
return_value_policy::reference and the keep_alive call policy */
|
||||
reference_internal
|
||||
};
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); }
|
||||
|
||||
// Returns the size as a multiple of sizeof(void *), rounded up.
|
||||
inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); }
|
||||
|
||||
inline std::string error_string();
|
||||
|
||||
/**
|
||||
* The space to allocate for simple layout instance holders (see below) in multiple of the size of
|
||||
* a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required
|
||||
* to holder either a std::unique_ptr or std::shared_ptr (which is almost always
|
||||
* sizeof(std::shared_ptr<T>)).
|
||||
*/
|
||||
constexpr size_t instance_simple_holder_in_ptrs() {
|
||||
static_assert(sizeof(std::shared_ptr<int>) >= sizeof(std::unique_ptr<int>),
|
||||
"pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs");
|
||||
return size_in_ptrs(sizeof(std::shared_ptr<int>));
|
||||
}
|
||||
|
||||
// Forward declarations
|
||||
struct type_info;
|
||||
struct value_and_holder;
|
||||
|
||||
/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof')
|
||||
struct instance {
|
||||
PyObject_HEAD
|
||||
/// Storage for pointers and holder; see simple_layout, below, for a description
|
||||
union {
|
||||
void *simple_value_holder[1 + instance_simple_holder_in_ptrs()];
|
||||
struct {
|
||||
void **values_and_holders;
|
||||
bool *holder_constructed;
|
||||
} nonsimple;
|
||||
};
|
||||
/// Weak references (needed for keep alive):
|
||||
PyObject *weakrefs;
|
||||
/// If true, the pointer is owned which means we're free to manage it with a holder.
|
||||
bool owned : 1;
|
||||
/**
|
||||
* An instance has two possible value/holder layouts.
|
||||
*
|
||||
* Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer
|
||||
* and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied
|
||||
* whenever there is no python-side multiple inheritance of bound C++ types *and* the type's
|
||||
* holder will fit in the default space (which is large enough to hold either a std::unique_ptr
|
||||
* or std::shared_ptr).
|
||||
*
|
||||
* Non-simple layout applies when using custom holders that require more space than `shared_ptr`
|
||||
* (which is typically the size of two pointers), or when multiple inheritance is used on the
|
||||
* python side. Non-simple layout allocates the required amount of memory to have multiple
|
||||
* bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a
|
||||
* pointer to allocated space of the required space to hold a a sequence of value pointers and
|
||||
* holders followed by a set of holder-constructed flags (1 byte each), i.e.
|
||||
* [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of
|
||||
* `sizeof(void *)`. `nonsimple.holder_constructed` is, for convenience, a pointer to the
|
||||
* beginning of the [bb...] block (but not independently allocated).
|
||||
*/
|
||||
bool simple_layout : 1;
|
||||
/// For simple layout, tracks whether the holder has been constructed
|
||||
bool simple_holder_constructed : 1;
|
||||
/// If true, get_internals().patients has an entry for this object
|
||||
bool has_patients : 1;
|
||||
|
||||
/// Initializes all of the above type/values/holders data
|
||||
void allocate_layout();
|
||||
|
||||
/// Destroys/deallocates all of the above
|
||||
void deallocate_layout();
|
||||
|
||||
/// Returns the value_and_holder wrapper for the given type (or the first, if `find_type`
|
||||
/// omitted)
|
||||
value_and_holder get_value_and_holder(const type_info *find_type = nullptr);
|
||||
};
|
||||
|
||||
static_assert(std::is_standard_layout<instance>::value, "Internal error: `pybind11::detail::instance` is not standard layout!");
|
||||
|
||||
struct overload_hash {
|
||||
inline size_t operator()(const std::pair<const PyObject *, const char *>& v) const {
|
||||
size_t value = std::hash<const void *>()(v.first);
|
||||
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
|
||||
// other stls, this means `typeid(A)` from one module won't equal `typeid(A)` from another module
|
||||
// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under
|
||||
// stdlibc++, 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 hasher and equality
|
||||
// functions that use the type name.
|
||||
#if defined(__GLIBCXX__)
|
||||
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_equal_to = std::equal_to<std::type_index>;
|
||||
#else
|
||||
inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) {
|
||||
return lhs.name() == rhs.name() ||
|
||||
std::strcmp(lhs.name(), rhs.name()) == 0;
|
||||
}
|
||||
struct type_hash {
|
||||
size_t operator()(const std::type_index &t) const {
|
||||
size_t hash = 5381;
|
||||
const char *ptr = t.name();
|
||||
while (auto c = static_cast<unsigned char>(*ptr++))
|
||||
hash = (hash * 33) ^ c;
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
struct type_equal_to {
|
||||
bool operator()(const std::type_index &lhs, const std::type_index &rhs) const {
|
||||
return lhs.name() == rhs.name() ||
|
||||
std::strcmp(lhs.name(), rhs.name()) == 0;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename value_type>
|
||||
using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>;
|
||||
|
||||
/// Internal data structure used to track registered instances and types
|
||||
struct internals {
|
||||
type_map<void *> registered_types_cpp; // std::type_index -> type_info
|
||||
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; // PyTypeObject* -> base type_info(s)
|
||||
std::unordered_multimap<const void *, instance*> registered_instances; // void * -> instance*
|
||||
std::unordered_set<std::pair<const PyObject *, const char *>, overload_hash> inactive_overload_cache;
|
||||
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
|
||||
std::unordered_map<const PyObject *, std::vector<PyObject *>> patients;
|
||||
std::forward_list<void (*) (std::exception_ptr)> registered_exception_translators;
|
||||
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions
|
||||
std::vector<PyObject *> loader_patient_stack; // Used by `loader_life_support`
|
||||
PyTypeObject *static_property_type;
|
||||
PyTypeObject *default_metaclass;
|
||||
PyObject *instance_base;
|
||||
#if defined(WITH_THREAD)
|
||||
decltype(PyThread_create_key()) tstate = 0; // Usually an int but a long on Cygwin64 with Python 3.x
|
||||
PyInterpreterState *istate = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Return a reference to the current 'internals' information
|
||||
inline internals &get_internals();
|
||||
|
||||
/// from __cpp_future__ import (convenient aliases from C++14/17)
|
||||
#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910)
|
||||
using std::enable_if_t;
|
||||
using std::conditional_t;
|
||||
using std::remove_cv_t;
|
||||
using std::remove_reference_t;
|
||||
#else
|
||||
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
|
||||
template <typename T> using remove_cv_t = typename std::remove_cv<T>::type;
|
||||
template <typename T> using remove_reference_t = typename std::remove_reference<T>::type;
|
||||
#endif
|
||||
|
||||
/// Index sequences
|
||||
#if defined(PYBIND11_CPP14)
|
||||
using std::index_sequence;
|
||||
using std::make_index_sequence;
|
||||
#else
|
||||
template<size_t ...> struct index_sequence { };
|
||||
template<size_t N, size_t ...S> struct make_index_sequence_impl : make_index_sequence_impl <N - 1, N - 1, S...> { };
|
||||
template<size_t ...S> struct make_index_sequence_impl <0, S...> { typedef index_sequence<S...> type; };
|
||||
template<size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type;
|
||||
#endif
|
||||
|
||||
/// Make an index sequence of the indices of true arguments
|
||||
template <typename ISeq, size_t, bool...> struct select_indices_impl { using type = ISeq; };
|
||||
template <size_t... IPrev, size_t I, bool B, bool... Bs> struct select_indices_impl<index_sequence<IPrev...>, I, B, Bs...>
|
||||
: select_indices_impl<conditional_t<B, index_sequence<IPrev..., I>, index_sequence<IPrev...>>, I + 1, Bs...> {};
|
||||
template <bool... Bs> using select_indices = typename select_indices_impl<index_sequence<>, 0, Bs...>::type;
|
||||
|
||||
/// Backports of std::bool_constant and std::negation to accomodate older compilers
|
||||
template <bool B> using bool_constant = std::integral_constant<bool, B>;
|
||||
template <typename T> struct negation : bool_constant<!T::value> { };
|
||||
|
||||
template <typename...> struct void_t_impl { using type = void; };
|
||||
template <typename... Ts> using void_t = typename void_t_impl<Ts...>::type;
|
||||
|
||||
/// Compile-time all/any/none of that check the boolean value of all template types
|
||||
#ifdef __cpp_fold_expressions
|
||||
template <class... Ts> using all_of = bool_constant<(Ts::value && ...)>;
|
||||
template <class... Ts> using any_of = bool_constant<(Ts::value || ...)>;
|
||||
#elif !defined(_MSC_VER)
|
||||
template <bool...> struct bools {};
|
||||
template <class... Ts> using all_of = std::is_same<
|
||||
bools<Ts::value..., true>,
|
||||
bools<true, Ts::value...>>;
|
||||
template <class... Ts> using any_of = negation<all_of<negation<Ts>...>>;
|
||||
#else
|
||||
// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit
|
||||
// at a slight loss of compilation efficiency).
|
||||
template <class... Ts> using all_of = std::conjunction<Ts...>;
|
||||
template <class... Ts> using any_of = std::disjunction<Ts...>;
|
||||
#endif
|
||||
template <class... Ts> using none_of = negation<any_of<Ts...>>;
|
||||
|
||||
template <class T, template<class> class... Predicates> using satisfies_all_of = all_of<Predicates<T>...>;
|
||||
template <class T, template<class> class... Predicates> using satisfies_any_of = any_of<Predicates<T>...>;
|
||||
template <class T, template<class> class... Predicates> using satisfies_none_of = none_of<Predicates<T>...>;
|
||||
|
||||
/// Strip the class from a method type
|
||||
template <typename T> struct remove_class { };
|
||||
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { typedef R type(A...); };
|
||||
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...) const> { typedef R type(A...); };
|
||||
|
||||
/// Helper template to strip away type modifiers
|
||||
template <typename T> struct intrinsic_type { typedef T type; };
|
||||
template <typename T> struct intrinsic_type<const T> { typedef typename intrinsic_type<T>::type type; };
|
||||
template <typename T> struct intrinsic_type<T*> { typedef typename intrinsic_type<T>::type type; };
|
||||
template <typename T> struct intrinsic_type<T&> { typedef typename intrinsic_type<T>::type type; };
|
||||
template <typename T> struct intrinsic_type<T&&> { typedef typename intrinsic_type<T>::type type; };
|
||||
template <typename T, size_t N> struct intrinsic_type<const T[N]> { typedef typename intrinsic_type<T>::type type; };
|
||||
template <typename T, size_t N> struct intrinsic_type<T[N]> { typedef typename intrinsic_type<T>::type type; };
|
||||
template <typename T> using intrinsic_t = typename intrinsic_type<T>::type;
|
||||
|
||||
/// Helper type to replace 'void' in some expressions
|
||||
struct void_type { };
|
||||
|
||||
/// Helper template which holds a list of types
|
||||
template <typename...> struct type_list { };
|
||||
|
||||
/// Compile-time integer sum
|
||||
#ifdef __cpp_fold_expressions
|
||||
template <typename... Ts> constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); }
|
||||
#else
|
||||
constexpr size_t constexpr_sum() { return 0; }
|
||||
template <typename T, typename... Ts>
|
||||
constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); }
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(constexpr_impl)
|
||||
/// Implementation details for constexpr functions
|
||||
constexpr int first(int i) { return i; }
|
||||
template <typename T, typename... Ts>
|
||||
constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); }
|
||||
|
||||
constexpr int last(int /*i*/, int result) { return result; }
|
||||
template <typename T, typename... Ts>
|
||||
constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); }
|
||||
NAMESPACE_END(constexpr_impl)
|
||||
|
||||
/// Return the index of the first type in Ts which satisfies Predicate<T>. Returns sizeof...(Ts) if
|
||||
/// none match.
|
||||
template <template<typename> class Predicate, typename... Ts>
|
||||
constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate<Ts>::value...); }
|
||||
|
||||
/// Return the index of the last type in Ts which satisfies Predicate<T>, or -1 if none match.
|
||||
template <template<typename> class Predicate, typename... Ts>
|
||||
constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate<Ts>::value...); }
|
||||
|
||||
/// Return the Nth element from the parameter pack
|
||||
template <size_t N, typename T, typename... Ts>
|
||||
struct pack_element { using type = typename pack_element<N - 1, Ts...>::type; };
|
||||
template <typename T, typename... Ts>
|
||||
struct pack_element<0, T, Ts...> { using type = T; };
|
||||
|
||||
/// Return the one and only type which matches the predicate, or Default if none match.
|
||||
/// If more than one type matches the predicate, fail at compile-time.
|
||||
template <template<typename> class Predicate, typename Default, typename... Ts>
|
||||
struct exactly_one {
|
||||
static constexpr auto found = constexpr_sum(Predicate<Ts>::value...);
|
||||
static_assert(found <= 1, "Found more than one type matching the predicate");
|
||||
|
||||
static constexpr auto index = found ? constexpr_first<Predicate, Ts...>() : 0;
|
||||
using type = conditional_t<found, typename pack_element<index, Ts...>::type, Default>;
|
||||
};
|
||||
template <template<typename> class P, typename Default>
|
||||
struct exactly_one<P, Default> { using type = Default; };
|
||||
|
||||
template <template<typename> class Predicate, typename Default, typename... Ts>
|
||||
using exactly_one_t = typename exactly_one<Predicate, Default, Ts...>::type;
|
||||
|
||||
/// Defer the evaluation of type T until types Us are instantiated
|
||||
template <typename T, typename... /*Us*/> struct deferred_type { using type = T; };
|
||||
template <typename T, typename... Us> using deferred_t = typename deferred_type<T, Us...>::type;
|
||||
|
||||
/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of<T, T>::value == false`,
|
||||
/// unlike `std::is_base_of`)
|
||||
template <typename Base, typename Derived> using is_strict_base_of = bool_constant<
|
||||
std::is_base_of<Base, Derived>::value && !std::is_same<Base, Derived>::value>;
|
||||
|
||||
template <template<typename...> class Base>
|
||||
struct is_template_base_of_impl {
|
||||
template <typename... Us> static std::true_type check(Base<Us...> *);
|
||||
static std::false_type check(...);
|
||||
};
|
||||
|
||||
/// Check if a template is the base of a type. For example:
|
||||
/// `is_template_base_of<Base, T>` is true if `struct T : Base<U> {}` where U can be anything
|
||||
template <template<typename...> class Base, typename T>
|
||||
#if !defined(_MSC_VER)
|
||||
using is_template_base_of = decltype(is_template_base_of_impl<Base>::check((remove_cv_t<T>*)nullptr));
|
||||
#else // MSVC2015 has trouble with decltype in template aliases
|
||||
struct is_template_base_of : decltype(is_template_base_of_impl<Base>::check((remove_cv_t<T>*)nullptr)) { };
|
||||
#endif
|
||||
|
||||
/// Check if T is an instantiation of the template `Class`. For example:
|
||||
/// `is_instantiation<shared_ptr, T>` is true if `T == shared_ptr<U>` where U can be anything.
|
||||
template <template<typename...> class Class, typename T>
|
||||
struct is_instantiation : std::false_type { };
|
||||
template <template<typename...> class Class, typename... Us>
|
||||
struct is_instantiation<Class, Class<Us...>> : std::true_type { };
|
||||
|
||||
/// Check if T is std::shared_ptr<U> where U can be anything
|
||||
template <typename T> using is_shared_ptr = is_instantiation<std::shared_ptr, T>;
|
||||
|
||||
/// Check if T looks like an input iterator
|
||||
template <typename T, typename = void> struct is_input_iterator : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_input_iterator<T, void_t<decltype(*std::declval<T &>()), decltype(++std::declval<T &>())>>
|
||||
: std::true_type {};
|
||||
|
||||
/// Ignore that a variable is unused in compiler warnings
|
||||
inline void ignore_unused(const int *) { }
|
||||
|
||||
/// Apply a function over each element of a parameter pack
|
||||
#ifdef __cpp_fold_expressions
|
||||
#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...)
|
||||
#else
|
||||
using expand_side_effects = bool[];
|
||||
#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false }
|
||||
#endif
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
/// Returns a named pointer that is shared among all extension modules (using the same
|
||||
/// pybind11 version) running in the current interpreter. Names starting with underscores
|
||||
/// are reserved for internal usage. Returns `nullptr` if no matching entry was found.
|
||||
inline PYBIND11_NOINLINE void* get_shared_data(const std::string& name) {
|
||||
auto& internals = detail::get_internals();
|
||||
auto it = internals.shared_data.find(name);
|
||||
return it != internals.shared_data.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
/// Set the shared data that can be later recovered by `get_shared_data()`.
|
||||
inline PYBIND11_NOINLINE void *set_shared_data(const std::string& name, void *data) {
|
||||
detail::get_internals().shared_data[name] = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// added to the shared data under the given name and a reference to it is returned.
|
||||
template<typename T> T& get_or_create_shared_data(const std::string& name) {
|
||||
auto& internals = detail::get_internals();
|
||||
auto it = internals.shared_data.find(name);
|
||||
T* ptr = (T*) (it != internals.shared_data.end() ? it->second : nullptr);
|
||||
if (!ptr) {
|
||||
ptr = new T();
|
||||
internals.shared_data[name] = ptr;
|
||||
}
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/// Fetch and hold an error which was already set in Python
|
||||
class error_already_set : public std::runtime_error {
|
||||
public:
|
||||
error_already_set() : std::runtime_error(detail::error_string()) {
|
||||
PyErr_Fetch(&type, &value, &trace);
|
||||
}
|
||||
|
||||
error_already_set(const error_already_set &) = delete;
|
||||
|
||||
error_already_set(error_already_set &&e)
|
||||
: std::runtime_error(e.what()), type(e.type), value(e.value),
|
||||
trace(e.trace) { e.type = e.value = e.trace = nullptr; }
|
||||
|
||||
inline ~error_already_set(); // implementation in pybind11.h
|
||||
|
||||
error_already_set& operator=(const error_already_set &) = delete;
|
||||
|
||||
/// Give the error back to Python
|
||||
void restore() { PyErr_Restore(type, value, trace); type = value = trace = nullptr; }
|
||||
|
||||
/// Clear the held Python error state (the C++ `what()` message remains intact)
|
||||
void clear() { restore(); PyErr_Clear(); }
|
||||
|
||||
/// Check if the trapped exception matches a given Python exception class
|
||||
bool matches(PyObject *ex) const { return PyErr_GivenExceptionMatches(ex, type); }
|
||||
|
||||
private:
|
||||
PyObject *type, *value, *trace;
|
||||
};
|
||||
|
||||
/// C++ bindings of builtin Python exceptions
|
||||
class builtin_exception : public std::runtime_error {
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
/// Set the error using the Python C API
|
||||
virtual void set_error() const = 0;
|
||||
};
|
||||
|
||||
#define PYBIND11_RUNTIME_EXCEPTION(name, type) \
|
||||
class name : public builtin_exception { public: \
|
||||
using builtin_exception::builtin_exception; \
|
||||
name() : name("") { } \
|
||||
void set_error() const override { PyErr_SetString(type, what()); } \
|
||||
};
|
||||
|
||||
PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration)
|
||||
PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error
|
||||
PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
|
||||
|
||||
[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); }
|
||||
[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); }
|
||||
|
||||
template <typename T, typename SFINAE = void> struct format_descriptor { };
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
// Returns the index of the given type in the type char array below, and in the list in numpy.h
|
||||
// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double;
|
||||
// complex float,double,long double. Note that the long double types only participate when long
|
||||
// double is actually longer than double (it isn't under MSVC).
|
||||
// NB: not only the string below but also complex.h and numpy.h rely on this order.
|
||||
template <typename T, typename SFINAE = void> struct is_fmt_numeric { static constexpr bool value = false; };
|
||||
template <typename T> struct is_fmt_numeric<T, enable_if_t<std::is_arithmetic<T>::value>> {
|
||||
static constexpr bool value = true;
|
||||
static constexpr int index = std::is_same<T, bool>::value ? 0 : 1 + (
|
||||
std::is_integral<T>::value ? detail::log2(sizeof(T))*2 + std::is_unsigned<T>::value : 8 + (
|
||||
std::is_same<T, double>::value ? 1 : std::is_same<T, long double>::value ? 2 : 0));
|
||||
};
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
template <typename T> struct format_descriptor<T, detail::enable_if_t<std::is_arithmetic<T>::value>> {
|
||||
static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric<T>::index];
|
||||
static constexpr const char value[2] = { c, '\0' };
|
||||
static std::string format() { return std::string(1, c); }
|
||||
};
|
||||
|
||||
template <typename T> constexpr const char format_descriptor<
|
||||
T, detail::enable_if_t<std::is_arithmetic<T>::value>>::value[2];
|
||||
|
||||
/// RAII wrapper that temporarily clears any Python error state
|
||||
struct error_scope {
|
||||
PyObject *type, *value, *trace;
|
||||
error_scope() { PyErr_Fetch(&type, &value, &trace); }
|
||||
~error_scope() { PyErr_Restore(type, value, trace); }
|
||||
};
|
||||
|
||||
/// Dummy destructor wrapper that can be used to expose classes with a private destructor
|
||||
struct nodelete { template <typename T> void operator()(T*) { } };
|
||||
|
||||
// overload_cast requires variable templates: C++14
|
||||
#if defined(PYBIND11_CPP14)
|
||||
#define PYBIND11_OVERLOAD_CAST 1
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
template <typename... Args>
|
||||
struct overload_cast_impl {
|
||||
template <typename Return>
|
||||
constexpr auto operator()(Return (*pf)(Args...)) const noexcept
|
||||
-> decltype(pf) { return pf; }
|
||||
|
||||
template <typename Return, typename Class>
|
||||
constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept
|
||||
-> decltype(pmf) { return pmf; }
|
||||
|
||||
template <typename Return, typename Class>
|
||||
constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept
|
||||
-> decltype(pmf) { return pmf; }
|
||||
};
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
/// Syntax sugar for resolving overloaded function pointers:
|
||||
/// - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
|
||||
/// - sweet: overload_cast<Arg0, Arg1, Arg2>(&Class::func)
|
||||
template <typename... Args>
|
||||
static constexpr detail::overload_cast_impl<Args...> overload_cast = {};
|
||||
// MSVC 2015 only accepts this particular initialization syntax for this variable template.
|
||||
|
||||
/// Const member function selector for overload_cast
|
||||
/// - regular: static_cast<Return (Class::*)(Arg) const>(&Class::func)
|
||||
/// - sweet: overload_cast<Arg>(&Class::func, const_)
|
||||
static constexpr auto const_ = std::true_type{};
|
||||
|
||||
#else // no overload_cast: providing something that static_assert-fails:
|
||||
template <typename... Args> struct overload_cast {
|
||||
static_assert(detail::deferred_t<std::false_type, Args...>::value,
|
||||
"pybind11::overload_cast<...> requires compiling in C++14 mode");
|
||||
};
|
||||
#endif // overload_cast
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from
|
||||
// any standard container (or C-style array) supporting std::begin/std::end, any singleton
|
||||
// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair.
|
||||
template <typename T>
|
||||
class any_container {
|
||||
std::vector<T> v;
|
||||
public:
|
||||
any_container() = default;
|
||||
|
||||
// Can construct from a pair of iterators
|
||||
template <typename It, typename = enable_if_t<is_input_iterator<It>::value>>
|
||||
any_container(It first, It last) : v(first, last) { }
|
||||
|
||||
// Implicit conversion constructor from any arbitrary container type with values convertible to T
|
||||
template <typename Container, typename = enable_if_t<std::is_convertible<decltype(*std::begin(std::declval<const Container &>())), T>::value>>
|
||||
any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { }
|
||||
|
||||
// initializer_list's aren't deducible, so don't get matched by the above template; we need this
|
||||
// to explicitly allow implicit conversion from one:
|
||||
template <typename TIn, typename = enable_if_t<std::is_convertible<TIn, T>::value>>
|
||||
any_container(const std::initializer_list<TIn> &c) : any_container(c.begin(), c.end()) { }
|
||||
|
||||
// Avoid copying if given an rvalue vector of the correct type.
|
||||
any_container(std::vector<T> &&v) : v(std::move(v)) { }
|
||||
|
||||
// Moves the vector out of an rvalue any_container
|
||||
operator std::vector<T> &&() && { return std::move(v); }
|
||||
|
||||
// Dereferencing obtains a reference to the underlying vector
|
||||
std::vector<T> &operator*() { return v; }
|
||||
const std::vector<T> &operator*() const { return v; }
|
||||
|
||||
// -> lets you call methods on the underlying vector
|
||||
std::vector<T> *operator->() { return &v; }
|
||||
const std::vector<T> *operator->() const { return &v; }
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
|
||||
|
||||
NAMESPACE_END(pybind11)
|
61
third-party/pybind11/include/pybind11/complex.h
vendored
Normal file
61
third-party/pybind11/include/pybind11/complex.h
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
pybind11/complex.h: Complex number support
|
||||
|
||||
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 "pybind11.h"
|
||||
#include <complex>
|
||||
|
||||
/// glibc defines I as a macro which breaks things, e.g., boost template names
|
||||
#ifdef I
|
||||
# undef I
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
template <typename T> struct format_descriptor<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
static constexpr const char c = format_descriptor<T>::c;
|
||||
static constexpr const char value[3] = { 'Z', c, '\0' };
|
||||
static std::string format() { return std::string(value); }
|
||||
};
|
||||
|
||||
template <typename T> constexpr const char format_descriptor<
|
||||
std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T> struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
static constexpr bool value = true;
|
||||
static constexpr int index = is_fmt_numeric<T>::index + 3;
|
||||
};
|
||||
|
||||
template <typename T> class type_caster<std::complex<T>> {
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!src)
|
||||
return false;
|
||||
if (!convert && !PyComplex_Check(src.ptr()))
|
||||
return false;
|
||||
Py_complex result = PyComplex_AsCComplex(src.ptr());
|
||||
if (result.real == -1.0 && PyErr_Occurred()) {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
value = std::complex<T>((T) result.real, (T) result.imag);
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const std::complex<T> &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
return PyComplex_FromDoubles((double) src.real(), (double) src.imag());
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(std::complex<T>, _("complex"));
|
||||
};
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(pybind11)
|
185
third-party/pybind11/include/pybind11/descr.h
vendored
Normal file
185
third-party/pybind11/include/pybind11/descr.h
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
pybind11/descr.h: Helper type for concatenating type signatures
|
||||
either at runtime (C++11) or compile time (C++14)
|
||||
|
||||
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 "common.h"
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
/* Concatenate type signatures at compile time using C++14 */
|
||||
#if defined(PYBIND11_CPP14) && !defined(_MSC_VER)
|
||||
#define PYBIND11_CONSTEXPR_DESCR
|
||||
|
||||
template <size_t Size1, size_t Size2> class descr {
|
||||
template <size_t Size1_, size_t Size2_> friend class descr;
|
||||
public:
|
||||
constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1])
|
||||
: descr(text, types,
|
||||
make_index_sequence<Size1>(),
|
||||
make_index_sequence<Size2>()) { }
|
||||
|
||||
constexpr const char *text() const { return m_text; }
|
||||
constexpr const std::type_info * const * types() const { return m_types; }
|
||||
|
||||
template <size_t OtherSize1, size_t OtherSize2>
|
||||
constexpr descr<Size1 + OtherSize1, Size2 + OtherSize2> operator+(const descr<OtherSize1, OtherSize2> &other) const {
|
||||
return concat(other,
|
||||
make_index_sequence<Size1>(),
|
||||
make_index_sequence<Size2>(),
|
||||
make_index_sequence<OtherSize1>(),
|
||||
make_index_sequence<OtherSize2>());
|
||||
}
|
||||
|
||||
protected:
|
||||
template <size_t... Indices1, size_t... Indices2>
|
||||
constexpr descr(
|
||||
char const (&text) [Size1+1],
|
||||
const std::type_info * const (&types) [Size2+1],
|
||||
index_sequence<Indices1...>, index_sequence<Indices2...>)
|
||||
: m_text{text[Indices1]..., '\0'},
|
||||
m_types{types[Indices2]..., nullptr } {}
|
||||
|
||||
template <size_t OtherSize1, size_t OtherSize2, size_t... Indices1,
|
||||
size_t... Indices2, size_t... OtherIndices1, size_t... OtherIndices2>
|
||||
constexpr descr<Size1 + OtherSize1, Size2 + OtherSize2>
|
||||
concat(const descr<OtherSize1, OtherSize2> &other,
|
||||
index_sequence<Indices1...>, index_sequence<Indices2...>,
|
||||
index_sequence<OtherIndices1...>, index_sequence<OtherIndices2...>) const {
|
||||
return descr<Size1 + OtherSize1, Size2 + OtherSize2>(
|
||||
{ m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' },
|
||||
{ m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr }
|
||||
);
|
||||
}
|
||||
|
||||
protected:
|
||||
char m_text[Size1 + 1];
|
||||
const std::type_info * m_types[Size2 + 1];
|
||||
};
|
||||
|
||||
template <size_t Size> constexpr descr<Size - 1, 0> _(char const(&text)[Size]) {
|
||||
return descr<Size - 1, 0>(text, { nullptr });
|
||||
}
|
||||
|
||||
template <size_t Rem, size_t... Digits> struct int_to_str : int_to_str<Rem/10, Rem%10, Digits...> { };
|
||||
template <size_t...Digits> struct int_to_str<0, Digits...> {
|
||||
static constexpr auto digits = descr<sizeof...(Digits), 0>({ ('0' + Digits)..., '\0' }, { nullptr });
|
||||
};
|
||||
|
||||
// Ternary description (like std::conditional)
|
||||
template <bool B, size_t Size1, size_t Size2>
|
||||
constexpr enable_if_t<B, descr<Size1 - 1, 0>> _(char const(&text1)[Size1], char const(&)[Size2]) {
|
||||
return _(text1);
|
||||
}
|
||||
template <bool B, size_t Size1, size_t Size2>
|
||||
constexpr enable_if_t<!B, descr<Size2 - 1, 0>> _(char const(&)[Size1], char const(&text2)[Size2]) {
|
||||
return _(text2);
|
||||
}
|
||||
template <bool B, size_t SizeA1, size_t SizeA2, size_t SizeB1, size_t SizeB2>
|
||||
constexpr enable_if_t<B, descr<SizeA1, SizeA2>> _(descr<SizeA1, SizeA2> d, descr<SizeB1, SizeB2>) { return d; }
|
||||
template <bool B, size_t SizeA1, size_t SizeA2, size_t SizeB1, size_t SizeB2>
|
||||
constexpr enable_if_t<!B, descr<SizeB1, SizeB2>> _(descr<SizeA1, SizeA2>, descr<SizeB1, SizeB2> d) { return d; }
|
||||
|
||||
template <size_t Size> auto constexpr _() -> decltype(int_to_str<Size / 10, Size % 10>::digits) {
|
||||
return int_to_str<Size / 10, Size % 10>::digits;
|
||||
}
|
||||
|
||||
template <typename Type> constexpr descr<1, 1> _() {
|
||||
return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr });
|
||||
}
|
||||
|
||||
inline constexpr descr<0, 0> concat() { return _(""); }
|
||||
template <size_t Size1, size_t Size2, typename... Args> auto constexpr concat(descr<Size1, Size2> descr) { return descr; }
|
||||
template <size_t Size1, size_t Size2, typename... Args> auto constexpr concat(descr<Size1, Size2> descr, Args&&... args) { return descr + _(", ") + concat(args...); }
|
||||
template <size_t Size1, size_t Size2> auto constexpr type_descr(descr<Size1, Size2> descr) { return _("{") + descr + _("}"); }
|
||||
|
||||
#define PYBIND11_DESCR constexpr auto
|
||||
|
||||
#else /* Simpler C++11 implementation based on run-time memory allocation and copying */
|
||||
|
||||
class descr {
|
||||
public:
|
||||
PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) {
|
||||
size_t nChars = len(text), nTypes = len(types);
|
||||
m_text = new char[nChars];
|
||||
m_types = new const std::type_info *[nTypes];
|
||||
memcpy(m_text, text, nChars * sizeof(char));
|
||||
memcpy(m_types, types, nTypes * sizeof(const std::type_info *));
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE descr operator+(descr &&d2) && {
|
||||
descr r;
|
||||
|
||||
size_t nChars1 = len(m_text), nTypes1 = len(m_types);
|
||||
size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types);
|
||||
|
||||
r.m_text = new char[nChars1 + nChars2 - 1];
|
||||
r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1];
|
||||
memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char));
|
||||
memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char));
|
||||
memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *));
|
||||
memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *));
|
||||
|
||||
delete[] m_text; delete[] m_types;
|
||||
delete[] d2.m_text; delete[] d2.m_types;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char *text() { return m_text; }
|
||||
const std::type_info * * types() { return m_types; }
|
||||
|
||||
protected:
|
||||
PYBIND11_NOINLINE descr() { }
|
||||
|
||||
template <typename T> static size_t len(const T *ptr) { // return length including null termination
|
||||
const T *it = ptr;
|
||||
while (*it++ != (T) 0)
|
||||
;
|
||||
return static_cast<size_t>(it - ptr);
|
||||
}
|
||||
|
||||
const std::type_info **m_types = nullptr;
|
||||
char *m_text = nullptr;
|
||||
};
|
||||
|
||||
/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */
|
||||
|
||||
PYBIND11_NOINLINE inline descr _(const char *text) {
|
||||
const std::type_info *types[1] = { nullptr };
|
||||
return descr(text, types);
|
||||
}
|
||||
|
||||
template <bool B> PYBIND11_NOINLINE enable_if_t<B, descr> _(const char *text1, const char *) { return _(text1); }
|
||||
template <bool B> PYBIND11_NOINLINE enable_if_t<!B, descr> _(char const *, const char *text2) { return _(text2); }
|
||||
template <bool B> PYBIND11_NOINLINE enable_if_t<B, descr> _(descr d, descr) { return d; }
|
||||
template <bool B> PYBIND11_NOINLINE enable_if_t<!B, descr> _(descr, descr d) { return d; }
|
||||
|
||||
template <typename Type> PYBIND11_NOINLINE descr _() {
|
||||
const std::type_info *types[2] = { &typeid(Type), nullptr };
|
||||
return descr("%", types);
|
||||
}
|
||||
|
||||
template <size_t Size> PYBIND11_NOINLINE descr _() {
|
||||
const std::type_info *types[1] = { nullptr };
|
||||
return descr(std::to_string(Size).c_str(), types);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE inline descr concat() { return _(""); }
|
||||
PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; }
|
||||
template <typename... Args> PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward<Args>(args)...); }
|
||||
PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); }
|
||||
|
||||
#define PYBIND11_DESCR ::pybind11::detail::descr
|
||||
#endif
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(pybind11)
|
610
third-party/pybind11/include/pybind11/eigen.h
vendored
Normal file
610
third-party/pybind11/include/pybind11/eigen.h
vendored
Normal file
@ -0,0 +1,610 @@
|
||||
/*
|
||||
pybind11/eigen.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"
|
||||
|
||||
#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"
|
||||
# if __GNUC__ >= 7
|
||||
# pragma GCC diagnostic ignored "-Wint-in-bool-context"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
#endif
|
||||
|
||||
// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit
|
||||
// move constructors that break things. We could detect this an explicitly copy, but an extra copy
|
||||
// of matrices seems highly undesirable.
|
||||
static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7");
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
// 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>;
|
||||
|
||||
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 PYBIND11_DESCR descriptor() {
|
||||
constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
constexpr bool show_order = is_eigen_dense_map<Type>::value;
|
||||
constexpr bool show_c_contiguous = show_order && requires_row_major;
|
||||
constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major;
|
||||
|
||||
return type_descr(_("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();
|
||||
|
||||
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 PYBIND11_DESCR name() { return 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;
|
||||
};
|
||||
|
||||
// Eigen Ref/Map classes have slightly different policy requirements, meaning we don't want to force
|
||||
// `move` when a Ref/Map rvalue is returned; we treat Ref<> sort of like a pointer (we care about
|
||||
// the underlying data, not the outer shell).
|
||||
template <typename Return>
|
||||
struct return_value_policy_override<Return, enable_if_t<is_eigen_dense_map<Return>::value>> {
|
||||
static return_value_policy policy(return_value_policy p) { return p; }
|
||||
};
|
||||
|
||||
// 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 PYBIND11_DESCR name() { return 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
|
||||
Array 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 PYBIND11_DESCR name() { return 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>> {
|
||||
typedef typename Type::Scalar Scalar;
|
||||
typedef remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())> StorageIndex;
|
||||
typedef typename Type::Index 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 (!obj.get_type().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() + _("]"));
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(pybind11)
|
||||
|
||||
#if defined(__GNUG__) || defined(__clang__)
|
||||
# pragma GCC diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
195
third-party/pybind11/include/pybind11/embed.h
vendored
Normal file
195
third-party/pybind11/include/pybind11/embed.h
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
pybind11/embed.h: Support for embedding the interpreter
|
||||
|
||||
Copyright (c) 2017 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 "pybind11.h"
|
||||
#include "eval.h"
|
||||
|
||||
#if defined(PYPY_VERSION)
|
||||
# error Embedding the interpreter is not supported with PyPy
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
# define PYBIND11_EMBEDDED_MODULE_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() { \
|
||||
pybind11_init_wrapper_##name(); \
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
Add a new module to the table of builtins for the interpreter. Must be
|
||||
defined in global scope. The first macro parameter is the name of the
|
||||
module (without quotes). The second parameter is the variable which will
|
||||
be used as the interface to add functions and classes to the module.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
PYBIND11_EMBEDDED_MODULE(example, m) {
|
||||
// ... initialize functions and classes here
|
||||
m.def("foo", []() {
|
||||
return "Hello, World!";
|
||||
});
|
||||
}
|
||||
\endrst */
|
||||
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||
static void pybind11_init_##name(pybind11::module &); \
|
||||
static PyObject *pybind11_init_wrapper_##name() { \
|
||||
auto m = pybind11::module(#name); \
|
||||
try { \
|
||||
pybind11_init_##name(m); \
|
||||
return m.ptr(); \
|
||||
} catch (pybind11::error_already_set &e) { \
|
||||
e.clear(); \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
} \
|
||||
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
pybind11::detail::embedded_module name(#name, pybind11_init_impl_##name); \
|
||||
void pybind11_init_##name(pybind11::module &variable)
|
||||
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
|
||||
struct embedded_module {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
using init_t = PyObject *(*)();
|
||||
#else
|
||||
using init_t = void (*)();
|
||||
#endif
|
||||
embedded_module(const char *name, init_t init) {
|
||||
if (Py_IsInitialized())
|
||||
pybind11_fail("Can't add new modules after the interpreter has been initialized");
|
||||
|
||||
auto result = PyImport_AppendInittab(name, init);
|
||||
if (result == -1)
|
||||
pybind11_fail("Insufficient memory to add a new module");
|
||||
}
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
/** \rst
|
||||
Initialize the Python interpreter. No other pybind11 or CPython API functions can be
|
||||
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
|
||||
optional parameter can be used to skip the registration of signal handlers (see the
|
||||
Python documentation for details). Calling this function again after the interpreter
|
||||
has already been initialized is a fatal error.
|
||||
\endrst */
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true) {
|
||||
if (Py_IsInitialized())
|
||||
pybind11_fail("The interpreter is already running");
|
||||
|
||||
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||
|
||||
// Make .py files in the working directory available by default
|
||||
auto sys_path = reinterpret_borrow<list>(module::import("sys").attr("path"));
|
||||
sys_path.append(".");
|
||||
}
|
||||
|
||||
/** \rst
|
||||
Shut down the Python interpreter. No pybind11 or CPython API functions can be called
|
||||
after this. In addition, pybind11 objects must not outlive the interpreter:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
{ // BAD
|
||||
py::initialize_interpreter();
|
||||
auto hello = py::str("Hello, World!");
|
||||
py::finalize_interpreter();
|
||||
} // <-- BOOM, hello's destructor is called after interpreter shutdown
|
||||
|
||||
{ // GOOD
|
||||
py::initialize_interpreter();
|
||||
{ // scoped
|
||||
auto hello = py::str("Hello, World!");
|
||||
} // <-- OK, hello is cleaned up properly
|
||||
py::finalize_interpreter();
|
||||
}
|
||||
|
||||
{ // BETTER
|
||||
py::scoped_interpreter guard{};
|
||||
auto hello = py::str("Hello, World!");
|
||||
}
|
||||
|
||||
.. warning::
|
||||
|
||||
The interpreter can be restarted by calling `initialize_interpreter` again.
|
||||
Modules created using pybind11 can be safely re-initialized. However, Python
|
||||
itself cannot completely unload binary extension modules and there are several
|
||||
caveats with regard to interpreter restarting. All the details can be found
|
||||
in the CPython documentation. In short, not all interpreter memory may be
|
||||
freed, either due to reference cycles or user-created global data.
|
||||
|
||||
\endrst */
|
||||
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
|
||||
// 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().
|
||||
detail::internals **internals_ptr_ptr = &detail::get_internals_ptr();
|
||||
// It could also be stashed in builtins, so look there too:
|
||||
if (builtins.contains(id) && isinstance<capsule>(builtins[id]))
|
||||
internals_ptr_ptr = capsule(builtins[id]);
|
||||
|
||||
Py_Finalize();
|
||||
|
||||
if (internals_ptr_ptr) {
|
||||
delete *internals_ptr_ptr;
|
||||
*internals_ptr_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/** \rst
|
||||
Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
|
||||
This a move-only guard and only a single instance can exist.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
int main() {
|
||||
py::scoped_interpreter guard{};
|
||||
py::print(Hello, World!);
|
||||
} // <-- interpreter shutdown
|
||||
\endrst */
|
||||
class scoped_interpreter {
|
||||
public:
|
||||
scoped_interpreter(bool init_signal_handlers = true) {
|
||||
initialize_interpreter(init_signal_handlers);
|
||||
}
|
||||
|
||||
scoped_interpreter(const scoped_interpreter &) = delete;
|
||||
scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; }
|
||||
scoped_interpreter &operator=(const scoped_interpreter &) = delete;
|
||||
scoped_interpreter &operator=(scoped_interpreter &&) = delete;
|
||||
|
||||
~scoped_interpreter() {
|
||||
if (is_valid)
|
||||
finalize_interpreter();
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_valid = true;
|
||||
};
|
||||
|
||||
NAMESPACE_END(pybind11)
|
117
third-party/pybind11/include/pybind11/eval.h
vendored
Normal file
117
third-party/pybind11/include/pybind11/eval.h
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
pybind11/exec.h: Support for evaluating Python expressions and statements
|
||||
from strings and files
|
||||
|
||||
Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and
|
||||
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 "pybind11.h"
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
enum eval_mode {
|
||||
/// Evaluate a string containing an isolated expression
|
||||
eval_expr,
|
||||
|
||||
/// Evaluate a string containing a single statement. Returns \c none
|
||||
eval_single_statement,
|
||||
|
||||
/// Evaluate a string containing a sequence of statement. Returns \c none
|
||||
eval_statements
|
||||
};
|
||||
|
||||
template <eval_mode mode = eval_expr>
|
||||
object eval(str expr, object global = globals(), object local = object()) {
|
||||
if (!local)
|
||||
local = global;
|
||||
|
||||
/* PyRun_String does not accept a PyObject / encoding specifier,
|
||||
this seems to be the only alternative */
|
||||
std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr;
|
||||
|
||||
int start;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_input; break;
|
||||
case eval_single_statement: start = Py_single_input; break;
|
||||
case eval_statements: start = Py_file_input; break;
|
||||
default: pybind11_fail("invalid evaluation mode");
|
||||
}
|
||||
|
||||
PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
|
||||
if (!result)
|
||||
throw error_already_set();
|
||||
return reinterpret_steal<object>(result);
|
||||
}
|
||||
|
||||
template <eval_mode mode = eval_expr, size_t N>
|
||||
object eval(const char (&s)[N], object global = globals(), object local = object()) {
|
||||
/* Support raw string literals by removing common leading whitespace */
|
||||
auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s))
|
||||
: str(s);
|
||||
return eval<mode>(expr, global, local);
|
||||
}
|
||||
|
||||
inline void exec(str expr, object global = globals(), object local = object()) {
|
||||
eval<eval_statements>(expr, global, local);
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void exec(const char (&s)[N], object global = globals(), object local = object()) {
|
||||
eval<eval_statements>(s, global, local);
|
||||
}
|
||||
|
||||
template <eval_mode mode = eval_statements>
|
||||
object eval_file(str fname, object global = globals(), object local = object()) {
|
||||
if (!local)
|
||||
local = global;
|
||||
|
||||
int start;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_input; break;
|
||||
case eval_single_statement: start = Py_single_input; break;
|
||||
case eval_statements: start = Py_file_input; break;
|
||||
default: pybind11_fail("invalid evaluation mode");
|
||||
}
|
||||
|
||||
int closeFile = 1;
|
||||
std::string fname_str = (std::string) fname;
|
||||
#if PY_VERSION_HEX >= 0x03040000
|
||||
FILE *f = _Py_fopen_obj(fname.ptr(), "r");
|
||||
#elif PY_VERSION_HEX >= 0x03000000
|
||||
FILE *f = _Py_fopen(fname.ptr(), "r");
|
||||
#else
|
||||
/* No unicode support in open() :( */
|
||||
auto fobj = reinterpret_steal<object>(PyFile_FromString(
|
||||
const_cast<char *>(fname_str.c_str()),
|
||||
const_cast<char*>("r")));
|
||||
FILE *f = nullptr;
|
||||
if (fobj)
|
||||
f = PyFile_AsFile(fobj.ptr());
|
||||
closeFile = 0;
|
||||
#endif
|
||||
if (!f) {
|
||||
PyErr_Clear();
|
||||
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
|
||||
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
|
||||
local.ptr());
|
||||
(void) closeFile;
|
||||
#else
|
||||
PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
|
||||
local.ptr(), closeFile);
|
||||
#endif
|
||||
|
||||
if (!result)
|
||||
throw error_already_set();
|
||||
return reinterpret_steal<object>(result);
|
||||
}
|
||||
|
||||
NAMESPACE_END(pybind11)
|
85
third-party/pybind11/include/pybind11/functional.h
vendored
Normal file
85
third-party/pybind11/include/pybind11/functional.h
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
pybind11/functional.h: std::function<> support
|
||||
|
||||
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 "pybind11.h"
|
||||
#include <functional>
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename Return, typename... Args>
|
||||
struct type_caster<std::function<Return(Args...)>> {
|
||||
using type = std::function<Return(Args...)>;
|
||||
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
||||
using function_type = Return (*) (Args...);
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (src.is_none()) {
|
||||
// Defer accepting None to other overloads (if we aren't in convert mode):
|
||||
if (!convert) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isinstance<function>(src))
|
||||
return false;
|
||||
|
||||
auto func = reinterpret_borrow<function>(src);
|
||||
|
||||
/*
|
||||
When passing a C++ function as an argument to another C++
|
||||
function via Python, every function call would normally involve
|
||||
a full C++ -> Python -> C++ roundtrip, which can be prohibitive.
|
||||
Here, we try to at least detect the case where the function is
|
||||
stateless (i.e. function pointer or lambda function without
|
||||
captured variables), in which case the roundtrip can be avoided.
|
||||
*/
|
||||
if (auto cfunc = func.cpp_function()) {
|
||||
auto c = reinterpret_borrow<capsule>(PyCFunction_GET_SELF(cfunc.ptr()));
|
||||
auto rec = (function_record *) c;
|
||||
|
||||
if (rec && rec->is_stateless &&
|
||||
same_type(typeid(function_type), *reinterpret_cast<const std::type_info *>(rec->data[1]))) {
|
||||
struct capture { function_type f; };
|
||||
value = ((capture *) &rec->data)->f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = [func](Args... args) -> Return {
|
||||
gil_scoped_acquire acq;
|
||||
object retval(func(std::forward<Args>(args)...));
|
||||
/* Visual studio 2015 parser issue: need parentheses around this expression */
|
||||
return (retval.template cast<Return>());
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) {
|
||||
if (!f_)
|
||||
return none().inc_ref();
|
||||
|
||||
auto result = f_.template target<function_type>();
|
||||
if (result)
|
||||
return cpp_function(*result, policy).release();
|
||||
else
|
||||
return cpp_function(std::forward<Func>(f_), policy).release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(type, _("Callable[[") +
|
||||
argument_loader<Args...>::arg_names() + _("], ") +
|
||||
make_caster<retval_type>::name() +
|
||||
_("]"));
|
||||
};
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
NAMESPACE_END(pybind11)
|
1598
third-party/pybind11/include/pybind11/numpy.h
vendored
Normal file
1598
third-party/pybind11/include/pybind11/numpy.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
167
third-party/pybind11/include/pybind11/operators.h
vendored
Normal file
167
third-party/pybind11/include/pybind11/operators.h
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
pybind11/operator.h: Metatemplates for operator overloading
|
||||
|
||||
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 "pybind11.h"
|
||||
|
||||
#if defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type()))
|
||||
#elif defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Enumeration with all supported operator types
|
||||
enum op_id : int {
|
||||
op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift,
|
||||
op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert,
|
||||
op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le,
|
||||
op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift,
|
||||
op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero,
|
||||
op_repr, op_truediv, op_itruediv
|
||||
};
|
||||
|
||||
enum op_type : int {
|
||||
op_l, /* base type on left */
|
||||
op_r, /* base type on right */
|
||||
op_u /* unary operator */
|
||||
};
|
||||
|
||||
struct self_t { };
|
||||
static const self_t self = self_t();
|
||||
|
||||
/// Type for an unused type slot
|
||||
struct undefined_t { };
|
||||
|
||||
/// Don't warn about an unused variable
|
||||
inline self_t __self() { return self; }
|
||||
|
||||
/// base template of operator implementations
|
||||
template <op_id, op_type, typename B, typename L, typename R> struct op_impl { };
|
||||
|
||||
/// Operator implementation generator
|
||||
template <op_id id, op_type ot, typename L, typename R> struct op_ {
|
||||
template <typename Class, typename... Extra> void execute(Class &cl, const Extra&... extra) const {
|
||||
using Base = typename Class::type;
|
||||
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (id == op_truediv || id == op_itruediv)
|
||||
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
|
||||
&op::execute, is_operator(), extra...);
|
||||
#endif
|
||||
}
|
||||
template <typename Class, typename... Extra> void execute_cast(Class &cl, const Extra&... extra) const {
|
||||
using Base = typename Class::type;
|
||||
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute_cast, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (id == op_truediv || id == op_itruediv)
|
||||
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
|
||||
&op::execute, is_operator(), extra...);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \
|
||||
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const* name() { return "__" #id "__"; } \
|
||||
static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \
|
||||
static B execute_cast(const L &l, const R &r) { return B(expr); } \
|
||||
}; \
|
||||
template <typename B, typename L, typename R> struct op_impl<op_##id, op_r, B, L, R> { \
|
||||
static char const* name() { return "__" #rid "__"; } \
|
||||
static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \
|
||||
static B execute_cast(const R &r, const L &l) { return B(expr); } \
|
||||
}; \
|
||||
inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) { \
|
||||
return op_<op_##id, op_l, self_t, self_t>(); \
|
||||
} \
|
||||
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
|
||||
return op_<op_##id, op_l, self_t, T>(); \
|
||||
} \
|
||||
template <typename T> op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) { \
|
||||
return op_<op_##id, op_r, T, self_t>(); \
|
||||
}
|
||||
|
||||
#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \
|
||||
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const* name() { return "__" #id "__"; } \
|
||||
static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \
|
||||
static B execute_cast(L &l, const R &r) { return B(expr); } \
|
||||
}; \
|
||||
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
|
||||
return op_<op_##id, op_l, self_t, T>(); \
|
||||
}
|
||||
|
||||
#define PYBIND11_UNARY_OPERATOR(id, op, expr) \
|
||||
template <typename B, typename L> struct op_impl<op_##id, op_u, B, L, undefined_t> { \
|
||||
static char const* name() { return "__" #id "__"; } \
|
||||
static auto execute(const L &l) -> decltype(expr) { return expr; } \
|
||||
static B execute_cast(const L &l) { return B(expr); } \
|
||||
}; \
|
||||
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \
|
||||
return op_<op_##id, op_u, self_t, undefined_t>(); \
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r)
|
||||
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
|
||||
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r)
|
||||
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
|
||||
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
|
||||
PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
|
||||
PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
|
||||
PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r)
|
||||
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
|
||||
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
|
||||
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
|
||||
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
|
||||
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
|
||||
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
|
||||
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
|
||||
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
|
||||
//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
|
||||
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
|
||||
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
|
||||
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
|
||||
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
|
||||
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
|
||||
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||
PYBIND11_UNARY_OPERATOR(int, int_, (int) l)
|
||||
PYBIND11_UNARY_OPERATOR(float, float_, (double) l)
|
||||
|
||||
#undef PYBIND11_BINARY_OPERATOR
|
||||
#undef PYBIND11_INPLACE_OPERATOR
|
||||
#undef PYBIND11_UNARY_OPERATOR
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
using detail::self;
|
||||
|
||||
NAMESPACE_END(pybind11)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
65
third-party/pybind11/include/pybind11/options.h
vendored
Normal file
65
third-party/pybind11/include/pybind11/options.h
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
pybind11/options.h: global settings that are configurable at runtime.
|
||||
|
||||
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 "common.h"
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
class options {
|
||||
public:
|
||||
|
||||
// Default RAII constructor, which leaves settings as they currently are.
|
||||
options() : previous_state(global_state()) {}
|
||||
|
||||
// Class is non-copyable.
|
||||
options(const options&) = delete;
|
||||
options& operator=(const options&) = delete;
|
||||
|
||||
// Destructor, which restores settings that were in effect before.
|
||||
~options() {
|
||||
global_state() = previous_state;
|
||||
}
|
||||
|
||||
// Setter methods (affect the global state):
|
||||
|
||||
options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; }
|
||||
|
||||
options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; }
|
||||
|
||||
options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; }
|
||||
|
||||
options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; }
|
||||
|
||||
// Getter methods (return the global state):
|
||||
|
||||
static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; }
|
||||
|
||||
static bool show_function_signatures() { return global_state().show_function_signatures; }
|
||||
|
||||
// This type is not meant to be allocated on the heap.
|
||||
void* operator new(size_t) = delete;
|
||||
|
||||
private:
|
||||
|
||||
struct state {
|
||||
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
|
||||
bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings.
|
||||
};
|
||||
|
||||
static state &global_state() {
|
||||
static state instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
state previous_state;
|
||||
};
|
||||
|
||||
NAMESPACE_END(pybind11)
|
1852
third-party/pybind11/include/pybind11/pybind11.h
vendored
Normal file
1852
third-party/pybind11/include/pybind11/pybind11.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1282
third-party/pybind11/include/pybind11/pytypes.h
vendored
Normal file
1282
third-party/pybind11/include/pybind11/pytypes.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
367
third-party/pybind11/include/pybind11/stl.h
vendored
Normal file
367
third-party/pybind11/include/pybind11/stl.h
vendored
Normal file
@ -0,0 +1,367 @@
|
||||
/*
|
||||
pybind11/stl.h: Transparent conversion for STL data types
|
||||
|
||||
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 "pybind11.h"
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <valarray>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
// std::optional (but including it in c++14 mode isn't allowed)
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<optional>)
|
||||
# include <optional>
|
||||
# define PYBIND11_HAS_OPTIONAL 1
|
||||
# endif
|
||||
// std::experimental::optional (but not allowed in c++11 mode)
|
||||
# if defined(PYBIND11_CPP14) && __has_include(<experimental/optional>)
|
||||
# include <experimental/optional>
|
||||
# define PYBIND11_HAS_EXP_OPTIONAL 1
|
||||
# endif
|
||||
// std::variant
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<variant>)
|
||||
# include <variant>
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
# endif
|
||||
#elif defined(_MSC_VER) && defined(PYBIND11_CPP17)
|
||||
# include <optional>
|
||||
# include <variant>
|
||||
# define PYBIND11_HAS_OPTIONAL 1
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for
|
||||
/// forwarding a container element). Typically used indirect via forwarded_type(), below.
|
||||
template <typename T, typename U>
|
||||
using forwarded_type = conditional_t<
|
||||
std::is_lvalue_reference<T>::value, remove_reference_t<U> &, remove_reference_t<U> &&>;
|
||||
|
||||
/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically
|
||||
/// used for forwarding a container's elements.
|
||||
template <typename T, typename U>
|
||||
forwarded_type<T, U> forward_like(U &&u) {
|
||||
return std::forward<detail::forwarded_type<T, U>>(std::forward<U>(u));
|
||||
}
|
||||
|
||||
template <typename Type, typename Key> struct set_caster {
|
||||
using type = Type;
|
||||
using key_conv = make_caster<Key>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<pybind11::set>(src))
|
||||
return false;
|
||||
auto s = reinterpret_borrow<pybind11::set>(src);
|
||||
value.clear();
|
||||
for (auto entry : s) {
|
||||
key_conv conv;
|
||||
if (!conv.load(entry, convert))
|
||||
return false;
|
||||
value.insert(cast_op<Key &&>(std::move(conv)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
pybind11::set s;
|
||||
for (auto &value: src) {
|
||||
auto value_ = reinterpret_steal<object>(key_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_ || !s.add(value_))
|
||||
return handle();
|
||||
}
|
||||
return s.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name() + _("]"));
|
||||
};
|
||||
|
||||
template <typename Type, typename Key, typename Value> struct map_caster {
|
||||
using key_conv = make_caster<Key>;
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<dict>(src))
|
||||
return false;
|
||||
auto d = reinterpret_borrow<dict>(src);
|
||||
value.clear();
|
||||
for (auto it : d) {
|
||||
key_conv kconv;
|
||||
value_conv vconv;
|
||||
if (!kconv.load(it.first.ptr(), convert) ||
|
||||
!vconv.load(it.second.ptr(), convert))
|
||||
return false;
|
||||
value.emplace(cast_op<Key &&>(std::move(kconv)), cast_op<Value &&>(std::move(vconv)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
dict d;
|
||||
for (auto &kv: src) {
|
||||
auto key = reinterpret_steal<object>(key_conv::cast(forward_like<T>(kv.first), policy, parent));
|
||||
auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy, parent));
|
||||
if (!key || !value)
|
||||
return handle();
|
||||
d[key] = value;
|
||||
}
|
||||
return d.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name() + _(", ") + value_conv::name() + _("]"));
|
||||
};
|
||||
|
||||
template <typename Type, typename Value> struct list_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<sequence>(src))
|
||||
return false;
|
||||
auto s = reinterpret_borrow<sequence>(src);
|
||||
value.clear();
|
||||
reserve_maybe(s, &value);
|
||||
for (auto it : s) {
|
||||
value_conv conv;
|
||||
if (!conv.load(it, convert))
|
||||
return false;
|
||||
value.push_back(cast_op<Value &&>(std::move(conv)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T = Type,
|
||||
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
|
||||
void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); }
|
||||
void reserve_maybe(sequence, void *) { }
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
list l(src.size());
|
||||
size_t index = 0;
|
||||
for (auto &value: src) {
|
||||
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_)
|
||||
return handle();
|
||||
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference
|
||||
}
|
||||
return l.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name() + _("]"));
|
||||
};
|
||||
|
||||
template <typename Type, typename Alloc> struct type_caster<std::vector<Type, Alloc>>
|
||||
: list_caster<std::vector<Type, Alloc>, Type> { };
|
||||
|
||||
template <typename Type, typename Alloc> struct type_caster<std::list<Type, Alloc>>
|
||||
: list_caster<std::list<Type, Alloc>, Type> { };
|
||||
|
||||
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0> struct array_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
private:
|
||||
template <bool R = Resizable>
|
||||
bool require_size(enable_if_t<R, size_t> size) {
|
||||
if (value.size() != size)
|
||||
value.resize(size);
|
||||
return true;
|
||||
}
|
||||
template <bool R = Resizable>
|
||||
bool require_size(enable_if_t<!R, size_t> size) {
|
||||
return size == Size;
|
||||
}
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<list>(src))
|
||||
return false;
|
||||
auto l = reinterpret_borrow<list>(src);
|
||||
if (!require_size(l.size()))
|
||||
return false;
|
||||
size_t ctr = 0;
|
||||
for (auto it : l) {
|
||||
value_conv conv;
|
||||
if (!conv.load(it, convert))
|
||||
return false;
|
||||
value[ctr++] = cast_op<Value &&>(std::move(conv));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
list l(src.size());
|
||||
size_t index = 0;
|
||||
for (auto &value: src) {
|
||||
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_)
|
||||
return handle();
|
||||
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference
|
||||
}
|
||||
return l.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name() + _<Resizable>(_(""), _("[") + _<Size>() + _("]")) + _("]"));
|
||||
};
|
||||
|
||||
template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>>
|
||||
: array_caster<std::array<Type, Size>, Type, false, Size> { };
|
||||
|
||||
template <typename Type> struct type_caster<std::valarray<Type>>
|
||||
: array_caster<std::valarray<Type>, Type, true> { };
|
||||
|
||||
template <typename Key, typename Compare, typename Alloc> struct type_caster<std::set<Key, Compare, Alloc>>
|
||||
: set_caster<std::set<Key, Compare, Alloc>, Key> { };
|
||||
|
||||
template <typename Key, typename Hash, typename Equal, typename Alloc> struct type_caster<std::unordered_set<Key, Hash, Equal, Alloc>>
|
||||
: set_caster<std::unordered_set<Key, Hash, Equal, Alloc>, Key> { };
|
||||
|
||||
template <typename Key, typename Value, typename Compare, typename Alloc> struct type_caster<std::map<Key, Value, Compare, Alloc>>
|
||||
: map_caster<std::map<Key, Value, Compare, Alloc>, Key, Value> { };
|
||||
|
||||
template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc> struct type_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>>
|
||||
: map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> { };
|
||||
|
||||
// This type caster is intended to be used for std::optional and std::experimental::optional
|
||||
template<typename T> struct optional_caster {
|
||||
using value_conv = make_caster<typename T::value_type>;
|
||||
|
||||
template <typename T_>
|
||||
static handle cast(T_ &&src, return_value_policy policy, handle parent) {
|
||||
if (!src)
|
||||
return none().inc_ref();
|
||||
return value_conv::cast(*std::forward<T_>(src), policy, parent);
|
||||
}
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!src) {
|
||||
return false;
|
||||
} else if (src.is_none()) {
|
||||
return true; // default-constructed value is already empty
|
||||
}
|
||||
value_conv inner_caster;
|
||||
if (!inner_caster.load(src, convert))
|
||||
return false;
|
||||
|
||||
value.emplace(cast_op<typename T::value_type &&>(std::move(inner_caster)));
|
||||
return true;
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name() + _("]"));
|
||||
};
|
||||
|
||||
#if PYBIND11_HAS_OPTIONAL
|
||||
template<typename T> struct type_caster<std::optional<T>>
|
||||
: public optional_caster<std::optional<T>> {};
|
||||
|
||||
template<> struct type_caster<std::nullopt_t>
|
||||
: public void_caster<std::nullopt_t> {};
|
||||
#endif
|
||||
|
||||
#if PYBIND11_HAS_EXP_OPTIONAL
|
||||
template<typename T> struct type_caster<std::experimental::optional<T>>
|
||||
: public optional_caster<std::experimental::optional<T>> {};
|
||||
|
||||
template<> struct type_caster<std::experimental::nullopt_t>
|
||||
: public void_caster<std::experimental::nullopt_t> {};
|
||||
#endif
|
||||
|
||||
/// Visit a variant and cast any found type to Python
|
||||
struct variant_caster_visitor {
|
||||
return_value_policy policy;
|
||||
handle parent;
|
||||
|
||||
template <typename T>
|
||||
handle operator()(T &&src) const {
|
||||
return make_caster<T>::cast(std::forward<T>(src), policy, parent);
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar
|
||||
/// `namespace::variant` types which provide a `namespace::visit()` function are handled here
|
||||
/// automatically using argument-dependent lookup. Users can provide specializations for other
|
||||
/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`.
|
||||
template <template<typename...> class Variant>
|
||||
struct visit_helper {
|
||||
template <typename... Args>
|
||||
static auto call(Args &&...args) -> decltype(visit(std::forward<Args>(args)...)) {
|
||||
return visit(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
/// Generic variant caster
|
||||
template <typename Variant> struct variant_caster;
|
||||
|
||||
template <template<typename...> class V, typename... Ts>
|
||||
struct variant_caster<V<Ts...>> {
|
||||
static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative.");
|
||||
|
||||
template <typename U, typename... Us>
|
||||
bool load_alternative(handle src, bool convert, type_list<U, Us...>) {
|
||||
auto caster = make_caster<U>();
|
||||
if (caster.load(src, convert)) {
|
||||
value = cast_op<U>(caster);
|
||||
return true;
|
||||
}
|
||||
return load_alternative(src, convert, type_list<Us...>{});
|
||||
}
|
||||
|
||||
bool load_alternative(handle, bool, type_list<>) { return false; }
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
// Do a first pass without conversions to improve constructor resolution.
|
||||
// E.g. `py::int_(1).cast<variant<double, int>>()` needs to fill the `int`
|
||||
// slot of the variant. Without two-pass loading `double` would be filled
|
||||
// because it appears first and a conversion is possible.
|
||||
if (convert && load_alternative(src, false, type_list<Ts...>{}))
|
||||
return true;
|
||||
return load_alternative(src, convert, type_list<Ts...>{});
|
||||
}
|
||||
|
||||
template <typename Variant>
|
||||
static handle cast(Variant &&src, return_value_policy policy, handle parent) {
|
||||
return visit_helper<V>::call(variant_caster_visitor{policy, parent},
|
||||
std::forward<Variant>(src));
|
||||
}
|
||||
|
||||
using Type = V<Ts...>;
|
||||
PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster<Ts>::name()...) + _("]"));
|
||||
};
|
||||
|
||||
#if PYBIND11_HAS_VARIANT
|
||||
template <typename... Ts>
|
||||
struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> { };
|
||||
#endif
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
|
||||
os << (std::string) str(obj);
|
||||
return os;
|
||||
}
|
||||
|
||||
NAMESPACE_END(pybind11)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
588
third-party/pybind11/include/pybind11/stl_bind.h
vendored
Normal file
588
third-party/pybind11/include/pybind11/stl_bind.h
vendored
Normal file
@ -0,0 +1,588 @@
|
||||
/*
|
||||
pybind11/std_bind.h: Binding generators for STL data types
|
||||
|
||||
Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob
|
||||
|
||||
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 "common.h"
|
||||
#include "operators.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
/* SFINAE helper class used by 'is_comparable */
|
||||
template <typename T> struct container_traits {
|
||||
template <typename T2> static std::true_type test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>())*);
|
||||
template <typename T2> static std::false_type test_comparable(...);
|
||||
template <typename T2> static std::true_type test_value(typename T2::value_type *);
|
||||
template <typename T2> static std::false_type test_value(...);
|
||||
template <typename T2> static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *);
|
||||
template <typename T2> static std::false_type test_pair(...);
|
||||
|
||||
static constexpr const bool is_comparable = std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value;
|
||||
static constexpr const bool is_pair = std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value;
|
||||
static constexpr const bool is_vector = std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value;
|
||||
static constexpr const bool is_element = !is_pair && !is_vector;
|
||||
};
|
||||
|
||||
/* Default: is_comparable -> std::false_type */
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct is_comparable : std::false_type { };
|
||||
|
||||
/* For non-map data structures, check whether operator== can be instantiated */
|
||||
template <typename T>
|
||||
struct is_comparable<
|
||||
T, enable_if_t<container_traits<T>::is_element &&
|
||||
container_traits<T>::is_comparable>>
|
||||
: std::true_type { };
|
||||
|
||||
/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */
|
||||
template <typename T>
|
||||
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> {
|
||||
static constexpr const bool value =
|
||||
is_comparable<typename T::value_type>::value;
|
||||
};
|
||||
|
||||
/* For pairs, recursively check the two data types */
|
||||
template <typename T>
|
||||
struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> {
|
||||
static constexpr const bool value =
|
||||
is_comparable<typename T::first_type>::value &&
|
||||
is_comparable<typename T::second_type>::value;
|
||||
};
|
||||
|
||||
/* Fallback functions */
|
||||
template <typename, typename, typename... Args> void vector_if_copy_constructible(const Args &...) { }
|
||||
template <typename, typename, typename... Args> void vector_if_equal_operator(const Args &...) { }
|
||||
template <typename, typename, typename... Args> void vector_if_insertion_operator(const Args &...) { }
|
||||
template <typename, typename, typename... Args> void vector_modifiers(const Args &...) { }
|
||||
|
||||
template<typename Vector, typename Class_>
|
||||
void vector_if_copy_constructible(enable_if_t<
|
||||
std::is_copy_constructible<Vector>::value &&
|
||||
std::is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
|
||||
|
||||
cl.def(init<const Vector &>(), "Copy constructor");
|
||||
}
|
||||
|
||||
template<typename Vector, typename Class_>
|
||||
void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_> &cl) {
|
||||
using T = typename Vector::value_type;
|
||||
|
||||
cl.def(self == self);
|
||||
cl.def(self != self);
|
||||
|
||||
cl.def("count",
|
||||
[](const Vector &v, const T &x) {
|
||||
return std::count(v.begin(), v.end(), x);
|
||||
},
|
||||
arg("x"),
|
||||
"Return the number of times ``x`` appears in the list"
|
||||
);
|
||||
|
||||
cl.def("remove", [](Vector &v, const T &x) {
|
||||
auto p = std::find(v.begin(), v.end(), x);
|
||||
if (p != v.end())
|
||||
v.erase(p);
|
||||
else
|
||||
throw value_error();
|
||||
},
|
||||
arg("x"),
|
||||
"Remove the first item from the list whose value is x. "
|
||||
"It is an error if there is no such item."
|
||||
);
|
||||
|
||||
cl.def("__contains__",
|
||||
[](const Vector &v, const T &x) {
|
||||
return std::find(v.begin(), v.end(), x) != v.end();
|
||||
},
|
||||
arg("x"),
|
||||
"Return true the container contains ``x``"
|
||||
);
|
||||
}
|
||||
|
||||
// Vector modifiers -- requires a copyable vector_type:
|
||||
// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems
|
||||
// silly to allow deletion but not insertion, so include them here too.)
|
||||
template <typename Vector, typename Class_>
|
||||
void vector_modifiers(enable_if_t<std::is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
|
||||
using T = typename Vector::value_type;
|
||||
using SizeType = typename Vector::size_type;
|
||||
using DiffType = typename Vector::difference_type;
|
||||
|
||||
cl.def("append",
|
||||
[](Vector &v, const T &value) { v.push_back(value); },
|
||||
arg("x"),
|
||||
"Add an item to the end of the list");
|
||||
|
||||
cl.def("__init__", [](Vector &v, iterable it) {
|
||||
new (&v) Vector();
|
||||
try {
|
||||
v.reserve(len(it));
|
||||
for (handle h : it)
|
||||
v.push_back(h.cast<T>());
|
||||
} catch (...) {
|
||||
v.~Vector();
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
cl.def("extend",
|
||||
[](Vector &v, const Vector &src) {
|
||||
v.insert(v.end(), src.begin(), src.end());
|
||||
},
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list"
|
||||
);
|
||||
|
||||
cl.def("insert",
|
||||
[](Vector &v, SizeType i, const T &x) {
|
||||
if (i > v.size())
|
||||
throw index_error();
|
||||
v.insert(v.begin() + (DiffType) i, x);
|
||||
},
|
||||
arg("i") , arg("x"),
|
||||
"Insert an item at a given position."
|
||||
);
|
||||
|
||||
cl.def("pop",
|
||||
[](Vector &v) {
|
||||
if (v.empty())
|
||||
throw index_error();
|
||||
T t = v.back();
|
||||
v.pop_back();
|
||||
return t;
|
||||
},
|
||||
"Remove and return the last item"
|
||||
);
|
||||
|
||||
cl.def("pop",
|
||||
[](Vector &v, SizeType i) {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
T t = v[i];
|
||||
v.erase(v.begin() + (DiffType) i);
|
||||
return t;
|
||||
},
|
||||
arg("i"),
|
||||
"Remove and return the item at index ``i``"
|
||||
);
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Vector &v, SizeType i, const T &t) {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
v[i] = t;
|
||||
}
|
||||
);
|
||||
|
||||
/// Slicing protocol
|
||||
cl.def("__getitem__",
|
||||
[](const Vector &v, slice slice) -> Vector * {
|
||||
size_t start, stop, step, slicelength;
|
||||
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
|
||||
throw error_already_set();
|
||||
|
||||
Vector *seq = new Vector();
|
||||
seq->reserve((size_t) slicelength);
|
||||
|
||||
for (size_t i=0; i<slicelength; ++i) {
|
||||
seq->push_back(v[start]);
|
||||
start += step;
|
||||
}
|
||||
return seq;
|
||||
},
|
||||
arg("s"),
|
||||
"Retrieve list elements using a slice object"
|
||||
);
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Vector &v, slice slice, const Vector &value) {
|
||||
size_t start, stop, step, slicelength;
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
|
||||
throw error_already_set();
|
||||
|
||||
if (slicelength != value.size())
|
||||
throw std::runtime_error("Left and right hand size of slice assignment have different sizes!");
|
||||
|
||||
for (size_t i=0; i<slicelength; ++i) {
|
||||
v[start] = value[i];
|
||||
start += step;
|
||||
}
|
||||
},
|
||||
"Assign list elements using a slice object"
|
||||
);
|
||||
|
||||
cl.def("__delitem__",
|
||||
[](Vector &v, SizeType i) {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
v.erase(v.begin() + DiffType(i));
|
||||
},
|
||||
"Delete the list elements at index ``i``"
|
||||
);
|
||||
|
||||
cl.def("__delitem__",
|
||||
[](Vector &v, slice slice) {
|
||||
size_t start, stop, step, slicelength;
|
||||
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
|
||||
throw error_already_set();
|
||||
|
||||
if (step == 1 && false) {
|
||||
v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength));
|
||||
} else {
|
||||
for (size_t i = 0; i < slicelength; ++i) {
|
||||
v.erase(v.begin() + DiffType(start));
|
||||
start += step - 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
"Delete list elements using a slice object"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>),
|
||||
// we have to access by copying; otherwise we return by reference.
|
||||
template <typename Vector> using vector_needs_copy = negation<
|
||||
std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>>;
|
||||
|
||||
// The usual case: access and iterate by reference
|
||||
template <typename Vector, typename Class_>
|
||||
void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl) {
|
||||
using T = typename Vector::value_type;
|
||||
using SizeType = typename Vector::size_type;
|
||||
using ItType = typename Vector::iterator;
|
||||
|
||||
cl.def("__getitem__",
|
||||
[](Vector &v, SizeType i) -> T & {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
return v[i];
|
||||
},
|
||||
return_value_policy::reference_internal // ref + keepalive
|
||||
);
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Vector &v) {
|
||||
return make_iterator<
|
||||
return_value_policy::reference_internal, ItType, ItType, T&>(
|
||||
v.begin(), v.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
);
|
||||
}
|
||||
|
||||
// The case for special objects, like std::vector<bool>, that have to be returned-by-copy:
|
||||
template <typename Vector, typename Class_>
|
||||
void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl) {
|
||||
using T = typename Vector::value_type;
|
||||
using SizeType = typename Vector::size_type;
|
||||
using ItType = typename Vector::iterator;
|
||||
cl.def("__getitem__",
|
||||
[](const Vector &v, SizeType i) -> T {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
return v[i];
|
||||
}
|
||||
);
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Vector &v) {
|
||||
return make_iterator<
|
||||
return_value_policy::copy, ItType, ItType, T>(
|
||||
v.begin(), v.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
);
|
||||
}
|
||||
|
||||
template <typename Vector, typename Class_> auto vector_if_insertion_operator(Class_ &cl, std::string const &name)
|
||||
-> decltype(std::declval<std::ostream&>() << std::declval<typename Vector::value_type>(), void()) {
|
||||
using size_type = typename Vector::size_type;
|
||||
|
||||
cl.def("__repr__",
|
||||
[name](Vector &v) {
|
||||
std::ostringstream s;
|
||||
s << name << '[';
|
||||
for (size_type i=0; i < v.size(); ++i) {
|
||||
s << v[i];
|
||||
if (i != v.size() - 1)
|
||||
s << ", ";
|
||||
}
|
||||
s << ']';
|
||||
return s.str();
|
||||
},
|
||||
"Return the canonical string representation of this list."
|
||||
);
|
||||
}
|
||||
|
||||
// Provide the buffer interface for vectors if we have data() and we have a format for it
|
||||
// GCC seems to have "void std::vector<bool>::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer
|
||||
template <typename Vector, typename = void>
|
||||
struct vector_has_data_and_format : std::false_type {};
|
||||
template <typename Vector>
|
||||
struct vector_has_data_and_format<Vector, enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(), std::declval<Vector>().data()), typename Vector::value_type*>::value>> : std::true_type {};
|
||||
|
||||
// Add the buffer interface to a vector
|
||||
template <typename Vector, typename Class_, typename... Args>
|
||||
enable_if_t<detail::any_of<std::is_same<Args, buffer_protocol>...>::value>
|
||||
vector_buffer(Class_& cl) {
|
||||
using T = typename Vector::value_type;
|
||||
|
||||
static_assert(vector_has_data_and_format<Vector>::value, "There is not an appropriate format descriptor for this vector");
|
||||
|
||||
// numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here
|
||||
format_descriptor<T>::format();
|
||||
|
||||
cl.def_buffer([](Vector& v) -> buffer_info {
|
||||
return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)});
|
||||
});
|
||||
|
||||
cl.def("__init__", [](Vector& vec, buffer buf) {
|
||||
auto info = buf.request();
|
||||
if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T)))
|
||||
throw type_error("Only valid 1D buffers can be copied to a vector");
|
||||
if (!detail::compare_buffer_info<T>::compare(info) || (ssize_t) sizeof(T) != info.itemsize)
|
||||
throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor<T>::format() + ")");
|
||||
new (&vec) Vector();
|
||||
vec.reserve((size_t) info.shape[0]);
|
||||
T *p = static_cast<T*>(info.ptr);
|
||||
ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
|
||||
T *end = p + info.shape[0] * step;
|
||||
for (; p != end; p += step)
|
||||
vec.push_back(*p);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename Vector, typename Class_, typename... Args>
|
||||
enable_if_t<!detail::any_of<std::is_same<Args, buffer_protocol>...>::value> vector_buffer(Class_&) {}
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
//
|
||||
// std::vector
|
||||
//
|
||||
template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
|
||||
class_<Vector, holder_type> bind_vector(module &m, std::string const &name, Args&&... args) {
|
||||
using Class_ = class_<Vector, holder_type>;
|
||||
|
||||
Class_ cl(m, name.c_str(), std::forward<Args>(args)...);
|
||||
|
||||
// Declare the buffer interface if a buffer_protocol() is passed in
|
||||
detail::vector_buffer<Vector, Class_, Args...>(cl);
|
||||
|
||||
cl.def(init<>());
|
||||
|
||||
// Register copy constructor (if possible)
|
||||
detail::vector_if_copy_constructible<Vector, Class_>(cl);
|
||||
|
||||
// Register comparison-related operators and functions (if possible)
|
||||
detail::vector_if_equal_operator<Vector, Class_>(cl);
|
||||
|
||||
// Register stream insertion operator (if possible)
|
||||
detail::vector_if_insertion_operator<Vector, Class_>(cl, name);
|
||||
|
||||
// Modifiers require copyable vector value type
|
||||
detail::vector_modifiers<Vector, Class_>(cl);
|
||||
|
||||
// Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive
|
||||
detail::vector_accessor<Vector, Class_>(cl);
|
||||
|
||||
cl.def("__bool__",
|
||||
[](const Vector &v) -> bool {
|
||||
return !v.empty();
|
||||
},
|
||||
"Check whether the list is nonempty"
|
||||
);
|
||||
|
||||
cl.def("__len__", &Vector::size);
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
// C++ style functions deprecated, leaving it here as an example
|
||||
cl.def(init<size_type>());
|
||||
|
||||
cl.def("resize",
|
||||
(void (Vector::*) (size_type count)) & Vector::resize,
|
||||
"changes the number of elements stored");
|
||||
|
||||
cl.def("erase",
|
||||
[](Vector &v, SizeType i) {
|
||||
if (i >= v.size())
|
||||
throw index_error();
|
||||
v.erase(v.begin() + i);
|
||||
}, "erases element at index ``i``");
|
||||
|
||||
cl.def("empty", &Vector::empty, "checks whether the container is empty");
|
||||
cl.def("size", &Vector::size, "returns the number of elements");
|
||||
cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end");
|
||||
cl.def("pop_back", &Vector::pop_back, "removes the last element");
|
||||
|
||||
cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements");
|
||||
cl.def("reserve", &Vector::reserve, "reserves storage");
|
||||
cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage");
|
||||
cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory");
|
||||
|
||||
cl.def("clear", &Vector::clear, "clears the contents");
|
||||
cl.def("swap", &Vector::swap, "swaps the contents");
|
||||
|
||||
cl.def("front", [](Vector &v) {
|
||||
if (v.size()) return v.front();
|
||||
else throw index_error();
|
||||
}, "access the first element");
|
||||
|
||||
cl.def("back", [](Vector &v) {
|
||||
if (v.size()) return v.back();
|
||||
else throw index_error();
|
||||
}, "access the last element ");
|
||||
|
||||
#endif
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// std::map, std::unordered_map
|
||||
//
|
||||
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
/* Fallback functions */
|
||||
template <typename, typename, typename... Args> void map_if_insertion_operator(const Args &...) { }
|
||||
template <typename, typename, typename... Args> void map_assignment(const Args &...) { }
|
||||
|
||||
// Map assignment when copy-assignable: just copy the value
|
||||
template <typename Map, typename Class_>
|
||||
void map_assignment(enable_if_t<std::is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Map &m, const KeyType &k, const MappedType &v) {
|
||||
auto it = m.find(k);
|
||||
if (it != m.end()) it->second = v;
|
||||
else m.emplace(k, v);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting
|
||||
template<typename Map, typename Class_>
|
||||
void map_assignment(enable_if_t<
|
||||
!std::is_copy_assignable<typename Map::mapped_type>::value &&
|
||||
std::is_copy_constructible<typename Map::mapped_type>::value,
|
||||
Class_> &cl) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Map &m, const KeyType &k, const MappedType &v) {
|
||||
// We can't use m[k] = v; because value type might not be default constructable
|
||||
auto r = m.emplace(k, v);
|
||||
if (!r.second) {
|
||||
// value type is not copy assignable so the only way to insert it is to erase it first...
|
||||
m.erase(r.first);
|
||||
m.emplace(k, v);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &cl, std::string const &name)
|
||||
-> decltype(std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void()) {
|
||||
|
||||
cl.def("__repr__",
|
||||
[name](Map &m) {
|
||||
std::ostringstream s;
|
||||
s << name << '{';
|
||||
bool f = false;
|
||||
for (auto const &kv : m) {
|
||||
if (f)
|
||||
s << ", ";
|
||||
s << kv.first << ": " << kv.second;
|
||||
f = true;
|
||||
}
|
||||
s << '}';
|
||||
return s.str();
|
||||
},
|
||||
"Return the canonical string representation of this map."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
|
||||
class_<Map, holder_type> bind_map(module &m, const std::string &name, Args&&... args) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
using Class_ = class_<Map, holder_type>;
|
||||
|
||||
Class_ cl(m, name.c_str(), std::forward<Args>(args)...);
|
||||
|
||||
cl.def(init<>());
|
||||
|
||||
// Register stream insertion operator (if possible)
|
||||
detail::map_if_insertion_operator<Map, Class_>(cl, name);
|
||||
|
||||
cl.def("__bool__",
|
||||
[](const Map &m) -> bool { return !m.empty(); },
|
||||
"Check whether the map is nonempty"
|
||||
);
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
);
|
||||
|
||||
cl.def("items",
|
||||
[](Map &m) { return make_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
);
|
||||
|
||||
cl.def("__getitem__",
|
||||
[](Map &m, const KeyType &k) -> MappedType & {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end())
|
||||
throw key_error();
|
||||
return it->second;
|
||||
},
|
||||
return_value_policy::reference_internal // ref + keepalive
|
||||
);
|
||||
|
||||
// Assignment provided only if the type is copyable
|
||||
detail::map_assignment<Map, Class_>(cl);
|
||||
|
||||
cl.def("__delitem__",
|
||||
[](Map &m, const KeyType &k) {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end())
|
||||
throw key_error();
|
||||
return m.erase(it);
|
||||
}
|
||||
);
|
||||
|
||||
cl.def("__len__", &Map::size);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
NAMESPACE_END(pybind11)
|
53
third-party/pybind11/include/pybind11/typeid.h
vendored
Normal file
53
third-party/pybind11/include/pybind11/typeid.h
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
pybind11/typeid.h: Compiler-independent access to type identifiers
|
||||
|
||||
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 <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#if defined(__GNUG__)
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
/// Erase all occurrences of a substring
|
||||
inline void erase_all(std::string &string, const std::string &search) {
|
||||
for (size_t pos = 0;;) {
|
||||
pos = string.find(search, pos);
|
||||
if (pos == std::string::npos) break;
|
||||
string.erase(pos, search.length());
|
||||
}
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE inline void clean_type_id(std::string &name) {
|
||||
#if defined(__GNUG__)
|
||||
int status = 0;
|
||||
std::unique_ptr<char, void (*)(void *)> res {
|
||||
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free };
|
||||
if (status == 0)
|
||||
name = res.get();
|
||||
#else
|
||||
detail::erase_all(name, "class ");
|
||||
detail::erase_all(name, "struct ");
|
||||
detail::erase_all(name, "enum ");
|
||||
#endif
|
||||
detail::erase_all(name, "pybind11::");
|
||||
}
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
/// Return a string representation of a C++ type
|
||||
template <typename T> static std::string type_id() {
|
||||
std::string name(typeid(T).name());
|
||||
detail::clean_type_id(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
NAMESPACE_END(pybind11)
|
11
third-party/pybind11/pybind11/__init__.py
vendored
Normal file
11
third-party/pybind11/pybind11/__init__.py
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
from ._version import version_info, __version__ # noqa: F401 imported but unused
|
||||
|
||||
|
||||
def get_include(*args, **kwargs):
|
||||
import os
|
||||
try:
|
||||
from pip import locations
|
||||
return os.path.dirname(
|
||||
locations.distutils_scheme('pybind11', *args, **kwargs)['headers'])
|
||||
except ImportError:
|
||||
return 'include'
|
31
third-party/pybind11/pybind11/__main__.py
vendored
Normal file
31
third-party/pybind11/pybind11/__main__.py
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import sysconfig
|
||||
|
||||
from . import get_include
|
||||
|
||||
|
||||
def print_includes():
|
||||
dirs = [sysconfig.get_path('include')]
|
||||
if sysconfig.get_path('platinclude') not in dirs:
|
||||
dirs.append(sysconfig.get_path('platinclude'))
|
||||
if get_include() not in dirs:
|
||||
dirs.append(get_include())
|
||||
print(' '.join('-I' + d for d in dirs))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog='python -m pybind11')
|
||||
parser.add_argument('--includes', action='store_true',
|
||||
help='Include flags for both pybind11 and Python headers.')
|
||||
args = parser.parse_args()
|
||||
if not sys.argv[1:]:
|
||||
parser.print_help()
|
||||
if args.includes:
|
||||
print_includes()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
2
third-party/pybind11/pybind11/_version.py
vendored
Normal file
2
third-party/pybind11/pybind11/_version.py
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
version_info = (2, 2, 'dev0')
|
||||
__version__ = '.'.join(map(str, version_info))
|
10
third-party/pybind11/setup.cfg
vendored
Normal file
10
third-party/pybind11/setup.cfg
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
[bdist_wheel]
|
||||
universal=1
|
||||
|
||||
[flake8]
|
||||
max-line-length = 99
|
||||
show_source = True
|
||||
exclude = .git, __pycache__, build, dist, docs, tools, venv
|
||||
ignore =
|
||||
# required for pretty matrix formating: multiple spaces after `,` and `[`
|
||||
E201, E241
|
86
third-party/pybind11/setup.py
vendored
Normal file
86
third-party/pybind11/setup.py
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Setup script for PyPI; use CMakeFile.txt to build extension modules
|
||||
|
||||
from setuptools import setup
|
||||
from pybind11 import __version__
|
||||
import os
|
||||
|
||||
# Prevent installation of pybind11 headers by setting
|
||||
# PYBIND11_USE_CMAKE.
|
||||
if os.environ.get('PYBIND11_USE_CMAKE'):
|
||||
headers = []
|
||||
else:
|
||||
headers = [
|
||||
'include/pybind11/attr.h',
|
||||
'include/pybind11/buffer_info.h',
|
||||
'include/pybind11/cast.h',
|
||||
'include/pybind11/chrono.h',
|
||||
'include/pybind11/class_support.h',
|
||||
'include/pybind11/common.h',
|
||||
'include/pybind11/complex.h',
|
||||
'include/pybind11/descr.h',
|
||||
'include/pybind11/eigen.h',
|
||||
'include/pybind11/embed.h',
|
||||
'include/pybind11/eval.h',
|
||||
'include/pybind11/functional.h',
|
||||
'include/pybind11/numpy.h',
|
||||
'include/pybind11/operators.h',
|
||||
'include/pybind11/options.h',
|
||||
'include/pybind11/pybind11.h',
|
||||
'include/pybind11/pytypes.h',
|
||||
'include/pybind11/stl.h',
|
||||
'include/pybind11/stl_bind.h',
|
||||
'include/pybind11/typeid.h'
|
||||
]
|
||||
|
||||
setup(
|
||||
name='pybind11',
|
||||
version=__version__,
|
||||
description='Seamless operability between C++11 and Python',
|
||||
author='Wenzel Jakob',
|
||||
author_email='wenzel.jakob@epfl.ch',
|
||||
url='https://github.com/wjakob/pybind11',
|
||||
download_url='https://github.com/wjakob/pybind11/tarball/v' + __version__,
|
||||
packages=['pybind11'],
|
||||
license='BSD',
|
||||
headers=headers,
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Intended Audience :: Developers',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
'Topic :: Utilities',
|
||||
'Programming Language :: C++',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.2',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'License :: OSI Approved :: BSD License'
|
||||
],
|
||||
keywords='C++11, Python bindings',
|
||||
long_description="""pybind11 is a lightweight header-only library that
|
||||
exposes C++ types in Python and vice versa, mainly to create Python bindings of
|
||||
existing C++ code. Its goals and syntax are similar to the excellent
|
||||
Boost.Python by David Abrahams: to minimize boilerplate code in traditional
|
||||
extension modules by inferring type information using compile-time
|
||||
introspection.
|
||||
|
||||
The main issue with Boost.Python-and the reason for creating such a similar
|
||||
project-is Boost. Boost is an enormously large and complex suite of utility
|
||||
libraries that works with almost every C++ compiler in existence. This
|
||||
compatibility has its cost: arcane template tricks and workarounds are
|
||||
necessary to support the oldest and buggiest of compiler specimens. Now that
|
||||
C++11-compatible compilers are widely available, this heavy machinery has
|
||||
become an excessively large and unnecessary dependency.
|
||||
|
||||
Think of this library as a tiny self-contained version of Boost.Python with
|
||||
everything stripped away that isn't relevant for binding generation. Without
|
||||
comments, the core header files only require ~4K lines of code and depend on
|
||||
Python (2.7 or 3.x, or PyPy2.7 >= 5.7) and the C++ standard library. This
|
||||
compact implementation was possible thanks to some of the new C++11 language
|
||||
features (specifically: tuples, lambda functions and variadic templates). Since
|
||||
its creation, this library has grown beyond Boost.Python in many ways, leading
|
||||
to dramatically simpler binding code in many common situations.""")
|
57
third-party/pybind11/tools/FindCatch.cmake
vendored
Normal file
57
third-party/pybind11/tools/FindCatch.cmake
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
# - Find the Catch test framework or download it (single header)
|
||||
#
|
||||
# This is a quick module for internal use. It assumes that Catch is
|
||||
# REQUIRED and that a minimum version is provided (not EXACT). If
|
||||
# a suitable version isn't found locally, the single header file
|
||||
# will be downloaded and placed in the build dir: PROJECT_BINARY_DIR.
|
||||
#
|
||||
# This code sets the following variables:
|
||||
# CATCH_INCLUDE_DIR - path to catch.hpp
|
||||
# CATCH_VERSION - version number
|
||||
|
||||
if(NOT Catch_FIND_VERSION)
|
||||
message(FATAL_ERROR "A version number must be specified.")
|
||||
elseif(Catch_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "This module assumes Catch is not required.")
|
||||
elseif(Catch_FIND_VERSION_EXACT)
|
||||
message(FATAL_ERROR "Exact version numbers are not supported, only minimum.")
|
||||
endif()
|
||||
|
||||
# Extract the version number from catch.hpp
|
||||
function(_get_catch_version)
|
||||
file(STRINGS "${CATCH_INCLUDE_DIR}/catch.hpp" version_line REGEX "Catch v.*" LIMIT_COUNT 1)
|
||||
if(version_line MATCHES "Catch v([0-9]+)\\.([0-9]+)\\.([0-9]+)")
|
||||
set(CATCH_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Download the single-header version of Catch
|
||||
function(_download_catch version destination_dir)
|
||||
message(STATUS "Downloading catch v${version}...")
|
||||
set(url https://github.com/philsquared/Catch/releases/download/v${version}/catch.hpp)
|
||||
file(DOWNLOAD ${url} "${destination_dir}/catch.hpp" STATUS status)
|
||||
list(GET status 0 error)
|
||||
if(error)
|
||||
message(FATAL_ERROR "Could not download ${url}")
|
||||
endif()
|
||||
set(CATCH_INCLUDE_DIR "${destination_dir}" CACHE INTERNAL "")
|
||||
endfunction()
|
||||
|
||||
# Look for catch locally
|
||||
find_path(CATCH_INCLUDE_DIR NAMES catch.hpp PATH_SUFFIXES catch)
|
||||
if(CATCH_INCLUDE_DIR)
|
||||
_get_catch_version()
|
||||
endif()
|
||||
|
||||
# Download the header if it wasn't found or if it's outdated
|
||||
if(NOT CATCH_VERSION OR CATCH_VERSION VERSION_LESS ${Catch_FIND_VERSION})
|
||||
if(DOWNLOAD_CATCH)
|
||||
_download_catch(${Catch_FIND_VERSION} "${PROJECT_BINARY_DIR}/catch/")
|
||||
_get_catch_version()
|
||||
else()
|
||||
set(CATCH_FOUND FALSE)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CATCH_FOUND TRUE)
|
81
third-party/pybind11/tools/FindEigen3.cmake
vendored
Normal file
81
third-party/pybind11/tools/FindEigen3.cmake
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
# - Try to find Eigen3 lib
|
||||
#
|
||||
# This module supports requiring a minimum version, e.g. you can do
|
||||
# find_package(Eigen3 3.1.2)
|
||||
# to require version 3.1.2 or newer of Eigen3.
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# EIGEN3_FOUND - system has eigen lib with correct version
|
||||
# EIGEN3_INCLUDE_DIR - the eigen include directory
|
||||
# EIGEN3_VERSION - eigen version
|
||||
|
||||
# Copyright (c) 2006, 2007 Montel Laurent, <montel@kde.org>
|
||||
# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael@free.fr>
|
||||
# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
|
||||
# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
|
||||
|
||||
if(NOT Eigen3_FIND_VERSION)
|
||||
if(NOT Eigen3_FIND_VERSION_MAJOR)
|
||||
set(Eigen3_FIND_VERSION_MAJOR 2)
|
||||
endif(NOT Eigen3_FIND_VERSION_MAJOR)
|
||||
if(NOT Eigen3_FIND_VERSION_MINOR)
|
||||
set(Eigen3_FIND_VERSION_MINOR 91)
|
||||
endif(NOT Eigen3_FIND_VERSION_MINOR)
|
||||
if(NOT Eigen3_FIND_VERSION_PATCH)
|
||||
set(Eigen3_FIND_VERSION_PATCH 0)
|
||||
endif(NOT Eigen3_FIND_VERSION_PATCH)
|
||||
|
||||
set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
|
||||
endif(NOT Eigen3_FIND_VERSION)
|
||||
|
||||
macro(_eigen3_check_version)
|
||||
file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
|
||||
|
||||
string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
|
||||
set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
|
||||
string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
|
||||
set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
|
||||
string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
|
||||
set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
|
||||
|
||||
set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
|
||||
if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
|
||||
set(EIGEN3_VERSION_OK FALSE)
|
||||
else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
|
||||
set(EIGEN3_VERSION_OK TRUE)
|
||||
endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
|
||||
|
||||
if(NOT EIGEN3_VERSION_OK)
|
||||
|
||||
message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
|
||||
"but at least version ${Eigen3_FIND_VERSION} is required")
|
||||
endif(NOT EIGEN3_VERSION_OK)
|
||||
endmacro(_eigen3_check_version)
|
||||
|
||||
if (EIGEN3_INCLUDE_DIR)
|
||||
|
||||
# in cache already
|
||||
_eigen3_check_version()
|
||||
set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
|
||||
|
||||
else (EIGEN3_INCLUDE_DIR)
|
||||
|
||||
find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
|
||||
PATHS
|
||||
${CMAKE_INSTALL_PREFIX}/include
|
||||
${KDE4_INCLUDE_DIR}
|
||||
PATH_SUFFIXES eigen3 eigen
|
||||
)
|
||||
|
||||
if(EIGEN3_INCLUDE_DIR)
|
||||
_eigen3_check_version()
|
||||
endif(EIGEN3_INCLUDE_DIR)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
|
||||
|
||||
mark_as_advanced(EIGEN3_INCLUDE_DIR)
|
||||
|
||||
endif(EIGEN3_INCLUDE_DIR)
|
||||
|
194
third-party/pybind11/tools/FindPythonLibsNew.cmake
vendored
Normal file
194
third-party/pybind11/tools/FindPythonLibsNew.cmake
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
# - Find python libraries
|
||||
# This module finds the libraries corresponding to the Python interpeter
|
||||
# FindPythonInterp provides.
|
||||
# This code sets the following variables:
|
||||
#
|
||||
# PYTHONLIBS_FOUND - have the Python libs been found
|
||||
# PYTHON_PREFIX - path to the Python installation
|
||||
# PYTHON_LIBRARIES - path to the python library
|
||||
# PYTHON_INCLUDE_DIRS - path to where Python.h is found
|
||||
# PYTHON_MODULE_EXTENSION - lib extension, e.g. '.so' or '.pyd'
|
||||
# PYTHON_MODULE_PREFIX - lib name prefix: usually an empty string
|
||||
# PYTHON_SITE_PACKAGES - path to installation site-packages
|
||||
# PYTHON_IS_DEBUG - whether the Python interpreter is a debug build
|
||||
#
|
||||
# Thanks to talljimbo for the patch adding the 'LDVERSION' config
|
||||
# variable usage.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2001-2009 Kitware, Inc.
|
||||
# Copyright 2012 Continuum Analytics, Inc.
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
|
||||
# nor the names of their contributors may be used to endorse or promote
|
||||
# products derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#=============================================================================
|
||||
|
||||
if(PYTHONLIBS_FOUND)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Use the Python interpreter to find the libs.
|
||||
if(PythonLibsNew_FIND_REQUIRED)
|
||||
find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} REQUIRED)
|
||||
else()
|
||||
find_package(PythonInterp ${PythonLibsNew_FIND_VERSION})
|
||||
endif()
|
||||
|
||||
if(NOT PYTHONINTERP_FOUND)
|
||||
set(PYTHONLIBS_FOUND FALSE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# According to http://stackoverflow.com/questions/646518/python-how-to-detect-debug-interpreter
|
||||
# testing whether sys has the gettotalrefcount function is a reliable, cross-platform
|
||||
# way to detect a CPython debug interpreter.
|
||||
#
|
||||
# The library suffix is from the config var LDVERSION sometimes, otherwise
|
||||
# VERSION. VERSION will typically be like "2.7" on unix, and "27" on windows.
|
||||
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
|
||||
"from distutils import sysconfig as s;import sys;import struct;
|
||||
print('.'.join(str(v) for v in sys.version_info));
|
||||
print(sys.prefix);
|
||||
print(s.get_python_inc(plat_specific=True));
|
||||
print(s.get_python_lib(plat_specific=True));
|
||||
print(s.get_config_var('SO'));
|
||||
print(hasattr(sys, 'gettotalrefcount')+0);
|
||||
print(struct.calcsize('@P'));
|
||||
print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION'));
|
||||
print(s.get_config_var('LIBDIR') or '');
|
||||
print(s.get_config_var('MULTIARCH') or '');
|
||||
"
|
||||
RESULT_VARIABLE _PYTHON_SUCCESS
|
||||
OUTPUT_VARIABLE _PYTHON_VALUES
|
||||
ERROR_VARIABLE _PYTHON_ERROR_VALUE)
|
||||
|
||||
if(NOT _PYTHON_SUCCESS MATCHES 0)
|
||||
if(PythonLibsNew_FIND_REQUIRED)
|
||||
message(FATAL_ERROR
|
||||
"Python config failure:\n${_PYTHON_ERROR_VALUE}")
|
||||
endif()
|
||||
set(PYTHONLIBS_FOUND FALSE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Convert the process output into a list
|
||||
string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES})
|
||||
string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES})
|
||||
list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST)
|
||||
list(GET _PYTHON_VALUES 1 PYTHON_PREFIX)
|
||||
list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR)
|
||||
list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES)
|
||||
list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION)
|
||||
list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG)
|
||||
list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P)
|
||||
list(GET _PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX)
|
||||
list(GET _PYTHON_VALUES 8 PYTHON_LIBDIR)
|
||||
list(GET _PYTHON_VALUES 9 PYTHON_MULTIARCH)
|
||||
|
||||
# Make sure the Python has the same pointer-size as the chosen compiler
|
||||
# Skip if CMAKE_SIZEOF_VOID_P is not defined
|
||||
if(CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}"))
|
||||
if(PythonLibsNew_FIND_REQUIRED)
|
||||
math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8")
|
||||
math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8")
|
||||
message(FATAL_ERROR
|
||||
"Python config failure: Python is ${_PYTHON_BITS}-bit, "
|
||||
"chosen compiler is ${_CMAKE_BITS}-bit")
|
||||
endif()
|
||||
set(PYTHONLIBS_FOUND FALSE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# The built-in FindPython didn't always give the version numbers
|
||||
string(REGEX REPLACE "\\." ";" _PYTHON_VERSION_LIST ${_PYTHON_VERSION_LIST})
|
||||
list(GET _PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR)
|
||||
list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR)
|
||||
list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH)
|
||||
|
||||
# Make sure all directory separators are '/'
|
||||
string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX ${PYTHON_PREFIX})
|
||||
string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR ${PYTHON_INCLUDE_DIR})
|
||||
string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES})
|
||||
|
||||
if(CMAKE_HOST_WIN32)
|
||||
set(PYTHON_LIBRARY
|
||||
"${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib")
|
||||
|
||||
# when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the
|
||||
# original python installation. They may be found relative to PYTHON_INCLUDE_DIR.
|
||||
if(NOT EXISTS "${PYTHON_LIBRARY}")
|
||||
get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY)
|
||||
set(PYTHON_LIBRARY
|
||||
"${_PYTHON_ROOT}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib")
|
||||
endif()
|
||||
|
||||
# raise an error if the python libs are still not found.
|
||||
if(NOT EXISTS "${PYTHON_LIBRARY}")
|
||||
message(FATAL_ERROR "Python libraries not found")
|
||||
endif()
|
||||
|
||||
else()
|
||||
if(PYTHON_MULTIARCH)
|
||||
set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}")
|
||||
else()
|
||||
set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}")
|
||||
endif()
|
||||
#message(STATUS "Searching for Python libs in ${_PYTHON_LIBS_SEARCH}")
|
||||
# Probably this needs to be more involved. It would be nice if the config
|
||||
# information the python interpreter itself gave us were more complete.
|
||||
find_library(PYTHON_LIBRARY
|
||||
NAMES "python${PYTHON_LIBRARY_SUFFIX}"
|
||||
PATHS ${_PYTHON_LIBS_SEARCH}
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
# If all else fails, just set the name/version and let the linker figure out the path.
|
||||
if(NOT PYTHON_LIBRARY)
|
||||
set(PYTHON_LIBRARY python${PYTHON_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
PYTHON_LIBRARY
|
||||
PYTHON_INCLUDE_DIR
|
||||
)
|
||||
|
||||
# We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the
|
||||
# cache entries because they are meant to specify the location of a single
|
||||
# library. We now set the variables listed by the documentation for this
|
||||
# module.
|
||||
SET(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}")
|
||||
SET(PYTHON_LIBRARIES "${PYTHON_LIBRARY}")
|
||||
SET(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}")
|
||||
|
||||
find_package_message(PYTHON
|
||||
"Found PythonLibs: ${PYTHON_LIBRARY}"
|
||||
"${PYTHON_EXECUTABLE}${PYTHON_VERSION}")
|
||||
|
||||
set(PYTHONLIBS_FOUND TRUE)
|
82
third-party/pybind11/tools/check-style.sh
vendored
Normal file
82
third-party/pybind11/tools/check-style.sh
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Script to check include/test code for common pybind11 code style errors.
|
||||
#
|
||||
# This script currently checks for
|
||||
#
|
||||
# 1. use of tabs instead of spaces
|
||||
# 2. MSDOS-style CRLF endings
|
||||
# 3. trailing spaces
|
||||
# 4. missing space between keyword and parenthesis, e.g.: for(, if(, while(
|
||||
# 5. Missing space between right parenthesis and brace, e.g. 'for (...){'
|
||||
# 6. opening brace on its own line. It should always be on the same line as the
|
||||
# if/while/for/do statment.
|
||||
#
|
||||
# Invoke as: tools/check-style.sh
|
||||
#
|
||||
|
||||
errors=0
|
||||
IFS=$'\n'
|
||||
found=
|
||||
# The mt=41 sets a red background for matched tabs:
|
||||
GREP_COLORS='mt=41' GREP_COLOR='41' grep $'\t' include tests/*.{cpp,py,h} docs/*.rst -rn --color=always |
|
||||
while read f; do
|
||||
if [ -z "$found" ]; then
|
||||
echo -e '\033[31m\033[01mError: found tabs instead of spaces in the following files:\033[0m'
|
||||
found=1
|
||||
errors=1
|
||||
fi
|
||||
|
||||
echo " $f"
|
||||
done
|
||||
|
||||
found=
|
||||
grep -IUlr $'\r' include tests/*.{cpp,py,h} docs/*.rst --color=always |
|
||||
while read f; do
|
||||
if [ -z "$found" ]; then
|
||||
echo -e '\033[31m\033[01mError: found CRLF characters in the following files:\033[0m'
|
||||
found=1
|
||||
errors=1
|
||||
fi
|
||||
|
||||
echo " $f"
|
||||
done
|
||||
|
||||
found=
|
||||
# The mt=41 sets a red background for matched trailing spaces
|
||||
GREP_COLORS='mt=41' GREP_COLOR='41' grep '[[:blank:]]\+$' include tests/*.{cpp,py,h} docs/*.rst -rn --color=always |
|
||||
while read f; do
|
||||
if [ -z "$found" ]; then
|
||||
echo -e '\033[31m\033[01mError: found trailing spaces in the following files:\033[0m'
|
||||
found=1
|
||||
errors=1
|
||||
fi
|
||||
|
||||
echo " $f"
|
||||
done
|
||||
|
||||
found=
|
||||
grep '\<\(if\|for\|while\|catch\)(\|){' include tests/*.{cpp,py,h} -rn --color=always |
|
||||
while read line; do
|
||||
if [ -z "$found" ]; then
|
||||
echo -e '\033[31m\033[01mError: found the following coding style problems:\033[0m'
|
||||
found=1
|
||||
errors=1
|
||||
fi
|
||||
|
||||
echo " $line"
|
||||
done
|
||||
|
||||
found=
|
||||
GREP_COLORS='mt=41' GREP_COLOR='41' grep '^\s*{\s*$' include docs/*.rst -rn --color=always |
|
||||
while read f; do
|
||||
if [ -z "$found" ]; then
|
||||
echo -e '\033[31m\033[01mError: braces should occur on the same line as the if/while/.. statement. Found issues in the following files: \033[0m'
|
||||
found=1
|
||||
errors=1
|
||||
fi
|
||||
|
||||
echo " $f"
|
||||
done
|
||||
|
||||
exit $errors
|
38
third-party/pybind11/tools/libsize.py
vendored
Normal file
38
third-party/pybind11/tools/libsize.py
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
from __future__ import print_function, division
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Internal build script for generating debugging test .so size.
|
||||
# Usage:
|
||||
# python libsize.py file.so save.txt -- displays the size of file.so and, if save.txt exists, compares it to the
|
||||
# size in it, then overwrites save.txt with the new size for future runs.
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit("Invalid arguments: usage: python libsize.py file.so save.txt")
|
||||
|
||||
lib = sys.argv[1]
|
||||
save = sys.argv[2]
|
||||
|
||||
if not os.path.exists(lib):
|
||||
sys.exit("Error: requested file ({}) does not exist".format(lib))
|
||||
|
||||
libsize = os.path.getsize(lib)
|
||||
|
||||
print("------", os.path.basename(lib), "file size:", libsize, end='')
|
||||
|
||||
if os.path.exists(save):
|
||||
with open(save) as sf:
|
||||
oldsize = int(sf.readline())
|
||||
|
||||
if oldsize > 0:
|
||||
change = libsize - oldsize
|
||||
if change == 0:
|
||||
print(" (no change)")
|
||||
else:
|
||||
print(" (change of {:+} bytes = {:+.2%})".format(change, change / oldsize))
|
||||
else:
|
||||
print()
|
||||
|
||||
with open(save, 'w') as sf:
|
||||
sf.write(str(libsize))
|
||||
|
304
third-party/pybind11/tools/mkdoc.py
vendored
Normal file
304
third-party/pybind11/tools/mkdoc.py
vendored
Normal file
@ -0,0 +1,304 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Syntax: mkdoc.py [-I<path> ..] [.. a list of header files ..]
|
||||
#
|
||||
# Extract documentation from C++ header files to use it in Python bindings
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
from clang import cindex
|
||||
from clang.cindex import CursorKind
|
||||
from collections import OrderedDict
|
||||
from threading import Thread, Semaphore
|
||||
from multiprocessing import cpu_count
|
||||
|
||||
RECURSE_LIST = [
|
||||
CursorKind.TRANSLATION_UNIT,
|
||||
CursorKind.NAMESPACE,
|
||||
CursorKind.CLASS_DECL,
|
||||
CursorKind.STRUCT_DECL,
|
||||
CursorKind.ENUM_DECL,
|
||||
CursorKind.CLASS_TEMPLATE
|
||||
]
|
||||
|
||||
PRINT_LIST = [
|
||||
CursorKind.CLASS_DECL,
|
||||
CursorKind.STRUCT_DECL,
|
||||
CursorKind.ENUM_DECL,
|
||||
CursorKind.ENUM_CONSTANT_DECL,
|
||||
CursorKind.CLASS_TEMPLATE,
|
||||
CursorKind.FUNCTION_DECL,
|
||||
CursorKind.FUNCTION_TEMPLATE,
|
||||
CursorKind.CONVERSION_FUNCTION,
|
||||
CursorKind.CXX_METHOD,
|
||||
CursorKind.CONSTRUCTOR,
|
||||
CursorKind.FIELD_DECL
|
||||
]
|
||||
|
||||
CPP_OPERATORS = {
|
||||
'<=': 'le', '>=': 'ge', '==': 'eq', '!=': 'ne', '[]': 'array',
|
||||
'+=': 'iadd', '-=': 'isub', '*=': 'imul', '/=': 'idiv', '%=':
|
||||
'imod', '&=': 'iand', '|=': 'ior', '^=': 'ixor', '<<=': 'ilshift',
|
||||
'>>=': 'irshift', '++': 'inc', '--': 'dec', '<<': 'lshift', '>>':
|
||||
'rshift', '&&': 'land', '||': 'lor', '!': 'lnot', '~': 'bnot',
|
||||
'&': 'band', '|': 'bor', '+': 'add', '-': 'sub', '*': 'mul', '/':
|
||||
'div', '%': 'mod', '<': 'lt', '>': 'gt', '=': 'assign', '()': 'call'
|
||||
}
|
||||
|
||||
CPP_OPERATORS = OrderedDict(
|
||||
sorted(CPP_OPERATORS.items(), key=lambda t: -len(t[0])))
|
||||
|
||||
job_count = cpu_count()
|
||||
job_semaphore = Semaphore(job_count)
|
||||
|
||||
output = []
|
||||
|
||||
def d(s):
|
||||
return s.decode('utf8')
|
||||
|
||||
|
||||
def sanitize_name(name):
|
||||
name = re.sub(r'type-parameter-0-([0-9]+)', r'T\1', name)
|
||||
for k, v in CPP_OPERATORS.items():
|
||||
name = name.replace('operator%s' % k, 'operator_%s' % v)
|
||||
name = re.sub('<.*>', '', name)
|
||||
name = ''.join([ch if ch.isalnum() else '_' for ch in name])
|
||||
name = re.sub('_$', '', re.sub('_+', '_', name))
|
||||
return '__doc_' + name
|
||||
|
||||
|
||||
def process_comment(comment):
|
||||
result = ''
|
||||
|
||||
# Remove C++ comment syntax
|
||||
leading_spaces = float('inf')
|
||||
for s in comment.expandtabs(tabsize=4).splitlines():
|
||||
s = s.strip()
|
||||
if s.startswith('/*'):
|
||||
s = s[2:].lstrip('*')
|
||||
elif s.endswith('*/'):
|
||||
s = s[:-2].rstrip('*')
|
||||
elif s.startswith('///'):
|
||||
s = s[3:]
|
||||
if s.startswith('*'):
|
||||
s = s[1:]
|
||||
if len(s) > 0:
|
||||
leading_spaces = min(leading_spaces, len(s) - len(s.lstrip()))
|
||||
result += s + '\n'
|
||||
|
||||
if leading_spaces != float('inf'):
|
||||
result2 = ""
|
||||
for s in result.splitlines():
|
||||
result2 += s[leading_spaces:] + '\n'
|
||||
result = result2
|
||||
|
||||
# Doxygen tags
|
||||
cpp_group = '([\w:]+)'
|
||||
param_group = '([\[\w:\]]+)'
|
||||
|
||||
s = result
|
||||
s = re.sub(r'\\c\s+%s' % cpp_group, r'``\1``', s)
|
||||
s = re.sub(r'\\a\s+%s' % cpp_group, r'*\1*', s)
|
||||
s = re.sub(r'\\e\s+%s' % cpp_group, r'*\1*', s)
|
||||
s = re.sub(r'\\em\s+%s' % cpp_group, r'*\1*', s)
|
||||
s = re.sub(r'\\b\s+%s' % cpp_group, r'**\1**', s)
|
||||
s = re.sub(r'\\ingroup\s+%s' % cpp_group, r'', s)
|
||||
s = re.sub(r'\\param%s?\s+%s' % (param_group, cpp_group),
|
||||
r'\n\n$Parameter ``\2``:\n\n', s)
|
||||
s = re.sub(r'\\tparam%s?\s+%s' % (param_group, cpp_group),
|
||||
r'\n\n$Template parameter ``\2``:\n\n', s)
|
||||
|
||||
for in_, out_ in {
|
||||
'return': 'Returns',
|
||||
'author': 'Author',
|
||||
'authors': 'Authors',
|
||||
'copyright': 'Copyright',
|
||||
'date': 'Date',
|
||||
'remark': 'Remark',
|
||||
'sa': 'See also',
|
||||
'see': 'See also',
|
||||
'extends': 'Extends',
|
||||
'throw': 'Throws',
|
||||
'throws': 'Throws'
|
||||
}.items():
|
||||
s = re.sub(r'\\%s\s*' % in_, r'\n\n$%s:\n\n' % out_, s)
|
||||
|
||||
s = re.sub(r'\\details\s*', r'\n\n', s)
|
||||
s = re.sub(r'\\brief\s*', r'', s)
|
||||
s = re.sub(r'\\short\s*', r'', s)
|
||||
s = re.sub(r'\\ref\s*', r'', s)
|
||||
|
||||
s = re.sub(r'\\code\s?(.*?)\s?\\endcode',
|
||||
r"```\n\1\n```\n", s, flags=re.DOTALL)
|
||||
|
||||
# HTML/TeX tags
|
||||
s = re.sub(r'<tt>(.*?)</tt>', r'``\1``', s, flags=re.DOTALL)
|
||||
s = re.sub(r'<pre>(.*?)</pre>', r"```\n\1\n```\n", s, flags=re.DOTALL)
|
||||
s = re.sub(r'<em>(.*?)</em>', r'*\1*', s, flags=re.DOTALL)
|
||||
s = re.sub(r'<b>(.*?)</b>', r'**\1**', s, flags=re.DOTALL)
|
||||
s = re.sub(r'\\f\$(.*?)\\f\$', r'$\1$', s, flags=re.DOTALL)
|
||||
s = re.sub(r'<li>', r'\n\n* ', s)
|
||||
s = re.sub(r'</?ul>', r'', s)
|
||||
s = re.sub(r'</li>', r'\n\n', s)
|
||||
|
||||
s = s.replace('``true``', '``True``')
|
||||
s = s.replace('``false``', '``False``')
|
||||
|
||||
# Re-flow text
|
||||
wrapper = textwrap.TextWrapper()
|
||||
wrapper.expand_tabs = True
|
||||
wrapper.replace_whitespace = True
|
||||
wrapper.drop_whitespace = True
|
||||
wrapper.width = 70
|
||||
wrapper.initial_indent = wrapper.subsequent_indent = ''
|
||||
|
||||
result = ''
|
||||
in_code_segment = False
|
||||
for x in re.split(r'(```)', s):
|
||||
if x == '```':
|
||||
if not in_code_segment:
|
||||
result += '```\n'
|
||||
else:
|
||||
result += '\n```\n\n'
|
||||
in_code_segment = not in_code_segment
|
||||
elif in_code_segment:
|
||||
result += x.strip()
|
||||
else:
|
||||
for y in re.split(r'(?: *\n *){2,}', x):
|
||||
wrapped = wrapper.fill(re.sub(r'\s+', ' ', y).strip())
|
||||
if len(wrapped) > 0 and wrapped[0] == '$':
|
||||
result += wrapped[1:] + '\n'
|
||||
wrapper.initial_indent = \
|
||||
wrapper.subsequent_indent = ' ' * 4
|
||||
else:
|
||||
if len(wrapped) > 0:
|
||||
result += wrapped + '\n\n'
|
||||
wrapper.initial_indent = wrapper.subsequent_indent = ''
|
||||
return result.rstrip().lstrip('\n')
|
||||
|
||||
|
||||
def extract(filename, node, prefix):
|
||||
if not (node.location.file is None or
|
||||
os.path.samefile(d(node.location.file.name), filename)):
|
||||
return 0
|
||||
if node.kind in RECURSE_LIST:
|
||||
sub_prefix = prefix
|
||||
if node.kind != CursorKind.TRANSLATION_UNIT:
|
||||
if len(sub_prefix) > 0:
|
||||
sub_prefix += '_'
|
||||
sub_prefix += d(node.spelling)
|
||||
for i in node.get_children():
|
||||
extract(filename, i, sub_prefix)
|
||||
if node.kind in PRINT_LIST:
|
||||
comment = d(node.raw_comment) if node.raw_comment is not None else ''
|
||||
comment = process_comment(comment)
|
||||
sub_prefix = prefix
|
||||
if len(sub_prefix) > 0:
|
||||
sub_prefix += '_'
|
||||
if len(node.spelling) > 0:
|
||||
name = sanitize_name(sub_prefix + d(node.spelling))
|
||||
global output
|
||||
output.append((name, filename, comment))
|
||||
|
||||
|
||||
class ExtractionThread(Thread):
|
||||
def __init__(self, filename, parameters):
|
||||
Thread.__init__(self)
|
||||
self.filename = filename
|
||||
self.parameters = parameters
|
||||
job_semaphore.acquire()
|
||||
|
||||
def run(self):
|
||||
print('Processing "%s" ..' % self.filename, file=sys.stderr)
|
||||
try:
|
||||
index = cindex.Index(
|
||||
cindex.conf.lib.clang_createIndex(False, True))
|
||||
tu = index.parse(self.filename, self.parameters)
|
||||
extract(self.filename, tu.cursor, '')
|
||||
finally:
|
||||
job_semaphore.release()
|
||||
|
||||
if __name__ == '__main__':
|
||||
parameters = ['-x', 'c++', '-std=c++11']
|
||||
filenames = []
|
||||
|
||||
if platform.system() == 'Darwin':
|
||||
dev_path = '/Applications/Xcode.app/Contents/Developer/'
|
||||
lib_dir = dev_path + 'Toolchains/XcodeDefault.xctoolchain/usr/lib/'
|
||||
sdk_dir = dev_path + 'Platforms/MacOSX.platform/Developer/SDKs'
|
||||
libclang = lib_dir + 'libclang.dylib'
|
||||
|
||||
if os.path.exists(libclang):
|
||||
cindex.Config.set_library_path(os.path.dirname(libclang))
|
||||
|
||||
if os.path.exists(sdk_dir):
|
||||
sysroot_dir = os.path.join(sdk_dir, next(os.walk(sdk_dir))[1][0])
|
||||
parameters.append('-isysroot')
|
||||
parameters.append(sysroot_dir)
|
||||
|
||||
for item in sys.argv[1:]:
|
||||
if item.startswith('-'):
|
||||
parameters.append(item)
|
||||
else:
|
||||
filenames.append(item)
|
||||
|
||||
if len(filenames) == 0:
|
||||
print('Syntax: %s [.. a list of header files ..]' % sys.argv[0])
|
||||
exit(-1)
|
||||
|
||||
print('''/*
|
||||
This file contains docstrings for the Python bindings.
|
||||
Do not edit! These were automatically extracted by mkdoc.py
|
||||
*/
|
||||
|
||||
#define __EXPAND(x) x
|
||||
#define __COUNT(_1, _2, _3, _4, _5, _6, _7, COUNT, ...) COUNT
|
||||
#define __VA_SIZE(...) __EXPAND(__COUNT(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1))
|
||||
#define __CAT1(a, b) a ## b
|
||||
#define __CAT2(a, b) __CAT1(a, b)
|
||||
#define __DOC1(n1) __doc_##n1
|
||||
#define __DOC2(n1, n2) __doc_##n1##_##n2
|
||||
#define __DOC3(n1, n2, n3) __doc_##n1##_##n2##_##n3
|
||||
#define __DOC4(n1, n2, n3, n4) __doc_##n1##_##n2##_##n3##_##n4
|
||||
#define __DOC5(n1, n2, n3, n4, n5) __doc_##n1##_##n2##_##n3##_##n4##_##n5
|
||||
#define __DOC6(n1, n2, n3, n4, n5, n6) __doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6
|
||||
#define __DOC7(n1, n2, n3, n4, n5, n6, n7) __doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6##_##n7
|
||||
#define DOC(...) __EXPAND(__EXPAND(__CAT2(__DOC, __VA_SIZE(__VA_ARGS__)))(__VA_ARGS__))
|
||||
|
||||
#if defined(__GNUG__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#endif
|
||||
''')
|
||||
|
||||
output.clear()
|
||||
for filename in filenames:
|
||||
thr = ExtractionThread(filename, parameters)
|
||||
thr.start()
|
||||
|
||||
print('Waiting for jobs to finish ..', file=sys.stderr)
|
||||
for i in range(job_count):
|
||||
job_semaphore.acquire()
|
||||
|
||||
name_ctr = 1
|
||||
name_prev = None
|
||||
for name, _, comment in list(sorted(output, key=lambda x: (x[0], x[1]))):
|
||||
if name == name_prev:
|
||||
name_ctr += 1
|
||||
name = name + "_%i" % name_ctr
|
||||
else:
|
||||
name_prev = name
|
||||
name_ctr = 1
|
||||
print('\nstatic const char *%s =%sR"doc(%s)doc";' %
|
||||
(name, '\n' if '\n' in comment else ' ', comment))
|
||||
|
||||
print('''
|
||||
#if defined(__GNUG__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
''')
|
100
third-party/pybind11/tools/pybind11Config.cmake.in
vendored
Normal file
100
third-party/pybind11/tools/pybind11Config.cmake.in
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
# pybind11Config.cmake
|
||||
# --------------------
|
||||
#
|
||||
# PYBIND11 cmake module.
|
||||
# This module sets the following variables in your project::
|
||||
#
|
||||
# pybind11_FOUND - true if pybind11 and all required components found on the system
|
||||
# pybind11_VERSION - pybind11 version in format Major.Minor.Release
|
||||
# pybind11_INCLUDE_DIRS - Directories where pybind11 and python headers are located.
|
||||
# pybind11_INCLUDE_DIR - Directory where pybind11 headers are located.
|
||||
# pybind11_DEFINITIONS - Definitions necessary to use pybind11, namely USING_pybind11.
|
||||
# pybind11_LIBRARIES - compile flags and python libraries (as needed) to link against.
|
||||
# pybind11_LIBRARY - empty.
|
||||
# CMAKE_MODULE_PATH - appends location of accompanying FindPythonLibsNew.cmake and
|
||||
# pybind11Tools.cmake modules.
|
||||
#
|
||||
#
|
||||
# Available components: None
|
||||
#
|
||||
#
|
||||
# Exported targets::
|
||||
#
|
||||
# If pybind11 is found, this module defines the following :prop_tgt:`IMPORTED`
|
||||
# interface library targets::
|
||||
#
|
||||
# pybind11::module - for extension modules
|
||||
# pybind11::embed - for embedding the Python interpreter
|
||||
#
|
||||
# Python headers, libraries (as needed by platform), and the C++ standard
|
||||
# are attached to the target. Set PythonLibsNew variables to influence
|
||||
# python detection and PYBIND11_CPP_STANDARD (-std=c++11 or -std=c++14) to
|
||||
# influence standard setting. ::
|
||||
#
|
||||
# find_package(pybind11 CONFIG REQUIRED)
|
||||
# message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}")
|
||||
#
|
||||
# # Create an extension module
|
||||
# add_library(mylib MODULE main.cpp)
|
||||
# target_link_libraries(mylib pybind11::module)
|
||||
#
|
||||
# # Or embed the Python interpreter into an executable
|
||||
# add_executable(myexe main.cpp)
|
||||
# target_link_libraries(myexe pybind11::embed)
|
||||
#
|
||||
# Suggested usage::
|
||||
#
|
||||
# find_package with version info is not recommended except for release versions. ::
|
||||
#
|
||||
# find_package(pybind11 CONFIG)
|
||||
# find_package(pybind11 2.0 EXACT CONFIG REQUIRED)
|
||||
#
|
||||
#
|
||||
# The following variables can be set to guide the search for this package::
|
||||
#
|
||||
# pybind11_DIR - CMake variable, set to directory containing this Config file
|
||||
# CMAKE_PREFIX_PATH - CMake variable, set to root directory of this package
|
||||
# PATH - environment variable, set to bin directory of this package
|
||||
# CMAKE_DISABLE_FIND_PACKAGE_pybind11 - CMake variable, disables
|
||||
# find_package(pybind11) when not REQUIRED, perhaps to force internal build
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set(PN pybind11)
|
||||
|
||||
# location of pybind11/pybind11.h
|
||||
set(${PN}_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@")
|
||||
|
||||
set(${PN}_LIBRARY "")
|
||||
set(${PN}_DEFINITIONS USING_${PN})
|
||||
|
||||
check_required_components(${PN})
|
||||
|
||||
# make detectable the FindPythonLibsNew.cmake module
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
include(pybind11Tools)
|
||||
|
||||
if(NOT (CMAKE_VERSION VERSION_LESS 3.0))
|
||||
#-----------------------------------------------------------------------------
|
||||
# Don't include targets if this file is being picked up by another
|
||||
# project which has already built this as a subproject
|
||||
#-----------------------------------------------------------------------------
|
||||
if(NOT TARGET ${PN}::pybind11)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/${PN}Targets.cmake")
|
||||
|
||||
find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} MODULE REQUIRED)
|
||||
set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PYTHON_INCLUDE_DIRS})
|
||||
set_property(TARGET ${PN}::embed APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES})
|
||||
if(WIN32 OR CYGWIN)
|
||||
set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES})
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}")
|
||||
|
||||
get_property(_iid TARGET ${PN}::pybind11 PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
|
||||
get_property(_ill TARGET ${PN}::module PROPERTY INTERFACE_LINK_LIBRARIES)
|
||||
set(${PN}_INCLUDE_DIRS ${_iid})
|
||||
set(${PN}_LIBRARIES ${_ico} ${_ill})
|
||||
endif()
|
||||
endif()
|
198
third-party/pybind11/tools/pybind11Tools.cmake
vendored
Normal file
198
third-party/pybind11/tools/pybind11Tools.cmake
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
# tools/pybind11Tools.cmake -- Build system for the pybind11 modules
|
||||
#
|
||||
# Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
|
||||
#
|
||||
# All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
# Add a CMake parameter for choosing a desired Python version
|
||||
if(NOT PYBIND11_PYTHON_VERSION)
|
||||
set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules")
|
||||
endif()
|
||||
|
||||
set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4)
|
||||
find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CMakeParseArguments)
|
||||
|
||||
if(NOT PYBIND11_CPP_STANDARD)
|
||||
if(NOT MSVC)
|
||||
check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG)
|
||||
|
||||
if (HAS_CPP14_FLAG)
|
||||
set(PYBIND11_CPP_STANDARD -std=c++14)
|
||||
else()
|
||||
check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG)
|
||||
if (HAS_CPP11_FLAG)
|
||||
set(PYBIND11_CPP_STANDARD -std=c++11)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!")
|
||||
endif()
|
||||
endif()
|
||||
elseif(MSVC)
|
||||
set(PYBIND11_CPP_STANDARD /std:c++14)
|
||||
endif()
|
||||
|
||||
set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING
|
||||
"C++ standard flag, e.g. -std=c++11, -std=c++14, /std:c++14. Defaults to C++14 mode." FORCE)
|
||||
endif()
|
||||
|
||||
# Checks whether the given CXX/linker flags can compile and link a cxx file. cxxflags and
|
||||
# linkerflags are lists of flags to use. The result variable is a unique variable name for each set
|
||||
# of flags: the compilation result will be cached base on the result variable. If the flags work,
|
||||
# sets them in cxxflags_out/linkerflags_out internal cache variables (in addition to ${result}).
|
||||
function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out linkerflags_out)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${linkerflags})
|
||||
check_cxx_compiler_flag("${cxxflags}" ${result})
|
||||
if (${result})
|
||||
set(${cxxflags_out} "${cxxflags}" CACHE INTERNAL "" FORCE)
|
||||
set(${linkerflags_out} "${linkerflags}" CACHE INTERNAL "" FORCE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Internal: find the appropriate link time optimization flags for this compiler
|
||||
function(_pybind11_add_lto_flags target_name prefer_thin_lto)
|
||||
if (NOT DEFINED PYBIND11_LTO_CXX_FLAGS)
|
||||
set(PYBIND11_LTO_CXX_FLAGS "" CACHE INTERNAL "")
|
||||
set(PYBIND11_LTO_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
set(cxx_append "")
|
||||
set(linker_append "")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE)
|
||||
# Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it
|
||||
set(linker_append ";$<$<CONFIG:MinSizeRel>:-O3>")
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
set(cxx_append ";-fno-fat-lto-objects")
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND prefer_thin_lto)
|
||||
_pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO_THIN
|
||||
"-flto=thin${cxx_append}" "-flto=thin${linker_append}"
|
||||
PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
|
||||
endif()
|
||||
|
||||
if (NOT HAS_FLTO_THIN)
|
||||
_pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO
|
||||
"-flto${cxx_append}" "-flto${linker_append}"
|
||||
PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
|
||||
endif()
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel")
|
||||
# Intel equivalent to LTO is called IPO
|
||||
_pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO
|
||||
"-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
|
||||
elseif(MSVC)
|
||||
# cmake only interprets libraries as linker flags when they start with a - (otherwise it
|
||||
# converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags
|
||||
# with - instead of /, even if it is a bit non-standard:
|
||||
_pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG
|
||||
"/GL" "-LTCG" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
|
||||
endif()
|
||||
|
||||
if (PYBIND11_LTO_CXX_FLAGS)
|
||||
message(STATUS "LTO enabled")
|
||||
else()
|
||||
message(STATUS "LTO disabled (not supported by the compiler and/or linker)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Enable LTO flags if found, except for Debug builds
|
||||
if (PYBIND11_LTO_CXX_FLAGS)
|
||||
target_compile_options(${target_name} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:${PYBIND11_LTO_CXX_FLAGS}>")
|
||||
endif()
|
||||
if (PYBIND11_LTO_LINKER_FLAGS)
|
||||
target_link_libraries(${target_name} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:${PYBIND11_LTO_LINKER_FLAGS}>")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Build a Python extension module:
|
||||
# pybind11_add_module(<name> [MODULE | SHARED] [EXCLUDE_FROM_ALL]
|
||||
# [NO_EXTRAS] [THIN_LTO] source1 [source2 ...])
|
||||
#
|
||||
function(pybind11_add_module target_name)
|
||||
set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS THIN_LTO)
|
||||
cmake_parse_arguments(ARG "${options}" "" "" ${ARGN})
|
||||
|
||||
if(ARG_MODULE AND ARG_SHARED)
|
||||
message(FATAL_ERROR "Can't be both MODULE and SHARED")
|
||||
elseif(ARG_SHARED)
|
||||
set(lib_type SHARED)
|
||||
else()
|
||||
set(lib_type MODULE)
|
||||
endif()
|
||||
|
||||
if(ARG_EXCLUDE_FROM_ALL)
|
||||
set(exclude_from_all EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS})
|
||||
|
||||
target_include_directories(${target_name}
|
||||
PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt
|
||||
PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config
|
||||
PRIVATE ${PYTHON_INCLUDE_DIRS})
|
||||
|
||||
# The prefix and extension are provided by FindPythonLibsNew.cmake
|
||||
set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}")
|
||||
set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}")
|
||||
|
||||
if(WIN32 OR CYGWIN)
|
||||
# Link against the Python shared library on Windows
|
||||
target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES})
|
||||
elseif(APPLE)
|
||||
# It's quite common to have multiple copies of the same Python version
|
||||
# installed on one's system. E.g.: one copy from the OS and another copy
|
||||
# that's statically linked into an application like Blender or Maya.
|
||||
# If we link our plugin library against the OS Python here and import it
|
||||
# into Blender or Maya later on, this will cause segfaults when multiple
|
||||
# conflicting Python instances are active at the same time (even when they
|
||||
# are of the same version).
|
||||
|
||||
# Windows is not affected by this issue since it handles DLL imports
|
||||
# differently. The solution for Linux and Mac OS is simple: we just don't
|
||||
# link against the Python library. The resulting shared library will have
|
||||
# missing symbols, but that's perfectly fine -- they will be resolved at
|
||||
# import time.
|
||||
|
||||
target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup")
|
||||
|
||||
if(ARG_SHARED)
|
||||
# Suppress CMake >= 3.0 warning for shared libraries
|
||||
set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Make sure C++11/14 are enabled
|
||||
target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD})
|
||||
|
||||
if(ARG_NO_EXTRAS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
_pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO})
|
||||
|
||||
if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug)
|
||||
# Set the default symbol visibility to hidden (very important to obtain small binaries)
|
||||
target_compile_options(${target_name} PRIVATE "-fvisibility=hidden")
|
||||
|
||||
# Strip unnecessary sections of the binary on Linux/Mac OS
|
||||
if(CMAKE_STRIP)
|
||||
if(APPLE)
|
||||
add_custom_command(TARGET ${target_name} POST_BUILD
|
||||
COMMAND ${CMAKE_STRIP} -x $<TARGET_FILE:${target_name}>)
|
||||
else()
|
||||
add_custom_command(TARGET ${target_name} POST_BUILD
|
||||
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:${target_name}>)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# /MP enables multithreaded builds (relevant when there are many files), /bigobj is
|
||||
# needed for bigger binding projects due to the limit to 64k addressable sections
|
||||
target_compile_options(${target_name} PRIVATE /MP /bigobj)
|
||||
endif()
|
||||
endfunction()
|
@ -1,23 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(xlntpyarrow)
|
||||
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
add_subdirectory(../third-party/pybind11 pybind11)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake")
|
||||
|
||||
find_package(Arrow)
|
||||
|
||||
if(NOT ARROW_FOUND)
|
||||
message(FATAL_ERROR "Arrow not found.")
|
||||
endif()
|
||||
|
||||
option(CONDA_ROOT "Path to Conda directory." "")
|
||||
pybind11_add_module(xlntpyarrow xlntpyarrow.cpp)
|
||||
|
||||
if(NOT CONDA_ROOT)
|
||||
message(FATAL_ERROR "Missing Conda root directory option (-D CONDA_ROOT).")
|
||||
endif()
|
||||
|
||||
# Replace backslash with forward slash since Python interprets it as an escaped character
|
||||
string(REGEX REPLACE "\\\\" "/" CONDA_ROOT ${CONDA_ROOT})
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/setup.py.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/setup.py")
|
||||
|
||||
add_custom_target(xlntpyarrow ALL
|
||||
COMMAND python setup.py install --record=record.txt --xlntlib=$<TARGET_FILE:xlnt>
|
||||
DEPENDS xlnt
|
||||
SOURCES xlntpyarrow.cpp setup.py.cmake methods.hpp methods.cpp)
|
||||
target_include_directories(xlntpyarrow
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../source
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../third-party/pybind11/include
|
||||
PRIVATE ${ARROW_INCLUDE_DIR})
|
||||
target_link_libraries(xlntpyarrow
|
||||
PRIVATE xlnt
|
||||
PRIVATE ${ARROW_SHARED_IMP_LIB}
|
||||
PRIVATE ${ARROW_PYTHON_SHARED_IMP_LIB})
|
||||
|
@ -1,322 +0,0 @@
|
||||
// Copyright (c) 2017 Thomas Fussell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE
|
||||
//
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4458)
|
||||
#include <arrow/api.h>
|
||||
#include <arrow/python/pyarrow.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
#include <Python.h> // must be included after Arrow
|
||||
|
||||
#include <detail/default_case.hpp>
|
||||
#include <detail/unicode.hpp>
|
||||
#include <python_streambuf.hpp>
|
||||
#include <xlnt/cell/cell.hpp>
|
||||
#include <xlnt/cell/cell_reference.hpp>
|
||||
#include <xlnt/workbook/streaming_workbook_reader.hpp>
|
||||
#include <xlnt/workbook/streaming_workbook_writer.hpp>
|
||||
#include <xlnt/worksheet/worksheet.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<arrow::ArrayBuilder> make_array_builder(xlnt::cell::type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case xlnt::cell::type::number:
|
||||
return std::unique_ptr<arrow::ArrayBuilder>(new arrow::DoubleBuilder(arrow::default_memory_pool(), arrow::float64()));
|
||||
case xlnt::cell::type::inline_string:
|
||||
case xlnt::cell::type::shared_string:
|
||||
case xlnt::cell::type::error:
|
||||
case xlnt::cell::type::formula_string:
|
||||
case xlnt::cell::type::empty:
|
||||
return std::unique_ptr<arrow::StringBuilder>(new arrow::StringBuilder(arrow::default_memory_pool()));
|
||||
case xlnt::cell::type::boolean:
|
||||
return std::unique_ptr<arrow::ArrayBuilder>(new arrow::BooleanBuilder(arrow::default_memory_pool(), std::make_shared<arrow::BooleanType>()));
|
||||
case xlnt::cell::type::date:
|
||||
return std::unique_ptr<arrow::Date32Builder>(new arrow::Date32Builder(arrow::default_memory_pool()));
|
||||
}
|
||||
|
||||
default_case(std::unique_ptr<arrow::ArrayBuilder>(nullptrptr));
|
||||
}
|
||||
|
||||
arrow::Field make_type_field(const std::string &name, xlnt::cell::type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case xlnt::cell::type::number:
|
||||
return arrow::Field(name, arrow::float64());
|
||||
case xlnt::cell::type::inline_string:
|
||||
case xlnt::cell::type::shared_string:
|
||||
case xlnt::cell::type::error:
|
||||
case xlnt::cell::type::formula_string:
|
||||
case xlnt::cell::type::empty:
|
||||
return arrow::Field(name, std::make_shared<arrow::StringType>());
|
||||
case xlnt::cell::type::boolean:
|
||||
return arrow::Field(name, arrow::boolean());
|
||||
case xlnt::cell::type::date:
|
||||
return arrow::Field(name, arrow::date32());
|
||||
}
|
||||
|
||||
default_case(arrow::Field("", arrow::nullptr()));
|
||||
}
|
||||
|
||||
} // namespace xlnt
|
||||
|
||||
bool import_pyarrow()
|
||||
{
|
||||
static bool imported = false;
|
||||
|
||||
if (!imported)
|
||||
{
|
||||
if (arrow::py::import_pyarrow() != 0)
|
||||
{
|
||||
if (PyErr_Occurred() != nullptr)
|
||||
{
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
imported = true;
|
||||
}
|
||||
}
|
||||
|
||||
return imported;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
PyObject *xlntpyarrow_xlsx2arrow(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static const char *keywords[] = { "io", "sheetname", "header", "skiprows",
|
||||
"skip_footer", "index_col", "names", "converters", "dtype", "true_values",
|
||||
"false_values", "parse_cols", "squeeze", "na_values", "thousands",
|
||||
"keep_default_na", "verbose", "convert_float", nullptr };
|
||||
static auto keywords_nc = const_cast<char **>(keywords);
|
||||
|
||||
PyObject *io = nullptr;
|
||||
PyObject *sheetname = nullptr;
|
||||
PyObject *header = nullptr;
|
||||
PyObject *skiprows = nullptr;
|
||||
auto skip_footer = 0;
|
||||
PyObject *index_col = nullptr;
|
||||
PyObject *names = nullptr;
|
||||
PyObject *converters = nullptr;
|
||||
PyObject *dtype = nullptr;
|
||||
PyObject *true_values = nullptr;
|
||||
PyObject *false_values = nullptr;
|
||||
PyObject *parse_cols = nullptr;
|
||||
auto squeeze = false;
|
||||
PyObject *na_values = nullptr;
|
||||
const char *thousands = nullptr;
|
||||
auto keep_default_va = false;
|
||||
auto verbose = false;
|
||||
auto convert_float = false;
|
||||
|
||||
std::cout << "here" << std::endl;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOOiOOOOOOOpOzppp", keywords_nc,
|
||||
&io, &sheetname, &header, &skiprows, &skip_footer, &index_col, &names,
|
||||
&converters, &dtype, &true_values, &false_values, &parse_cols, &squeeze,
|
||||
&na_values, &thousands, &keep_default_va, &verbose, &convert_float))
|
||||
{
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
std::cout << "here2" << std::endl;
|
||||
|
||||
if (!import_pyarrow())
|
||||
{
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
std::cout << "here3" << std::endl;
|
||||
|
||||
// arg #1, io
|
||||
xlnt::python_streambuf file_buffer(io);
|
||||
std::istream file_stream(&file_buffer);
|
||||
|
||||
xlnt::streaming_workbook_reader reader;
|
||||
reader.open(file_stream);
|
||||
|
||||
std::cout << "here4" << std::endl;
|
||||
|
||||
// arg #2, sheetname
|
||||
auto sheet_titles = reader.sheet_titles();
|
||||
auto sheet_title = sheet_titles.front();
|
||||
|
||||
std::cout << "here5 " << sheet_title << std::endl;
|
||||
|
||||
if (sheetname != nullptr)
|
||||
{
|
||||
std::cout << "sheetname" << std::endl;
|
||||
|
||||
if (PyLong_Check(sheetname))
|
||||
{
|
||||
std::cout << "is long" << std::endl;
|
||||
// handle int sheetname
|
||||
auto sheet_index = PyLong_AsLong(sheetname);
|
||||
sheet_title = sheet_titles.at(sheet_index);
|
||||
}
|
||||
else if (PyUnicode_Check(sheetname))
|
||||
{
|
||||
std::cout << "is string" << std::endl;
|
||||
// handle string sheetname
|
||||
sheet_title = std::string(reinterpret_cast<char *>(PyUnicode_1BYTE_DATA(sheetname)));
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << sheet_title << std::endl;
|
||||
reader.begin_worksheet(sheet_title);
|
||||
|
||||
auto column_names = std::vector<std::string>();
|
||||
auto columns = std::vector<std::unique_ptr<arrow::ArrayBuilder>>();
|
||||
auto fields = std::vector<std::shared_ptr<arrow::Field>>();
|
||||
|
||||
auto arrow_check = [](arrow::Status s)
|
||||
{
|
||||
if (!s.ok())
|
||||
{
|
||||
throw xlnt::exception("conversion error");
|
||||
}
|
||||
};
|
||||
|
||||
while (reader.has_cell())
|
||||
{
|
||||
auto cell = reader.read_cell();
|
||||
|
||||
if (cell.row() == 1)
|
||||
{
|
||||
column_names.push_back(cell.value<std::string>());
|
||||
continue;
|
||||
}
|
||||
else if (cell.row() == 2)
|
||||
{
|
||||
auto column_name = column_names.at(cell.column().index - 1);
|
||||
auto field = make_type_field(column_name, cell.data_type());
|
||||
fields.push_back(std::make_shared<arrow::Field>(field));
|
||||
columns.push_back(make_array_builder(cell.data_type()));
|
||||
}
|
||||
|
||||
auto builder = columns.at(cell.column().index - 1).get();
|
||||
|
||||
switch (cell.data_type())
|
||||
{
|
||||
case xlnt::cell::type::number:
|
||||
{
|
||||
auto typed_builder = static_cast<arrow::DoubleBuilder*>(builder);
|
||||
typed_builder->Append(0);
|
||||
break;
|
||||
}
|
||||
case xlnt::cell::type::inline_string:
|
||||
case xlnt::cell::type::shared_string:
|
||||
case xlnt::cell::type::error:
|
||||
case xlnt::cell::type::formula_string:
|
||||
case xlnt::cell::type::empty:
|
||||
{
|
||||
auto typed_builder = static_cast<arrow::StringBuilder*>(builder);
|
||||
typed_builder->Append(cell.value<std::string>());
|
||||
break;
|
||||
}
|
||||
case xlnt::cell::type::boolean:
|
||||
{
|
||||
auto typed_builder = static_cast<arrow::BooleanBuilder*>(builder);
|
||||
typed_builder->Append(cell.value<bool>());
|
||||
break;
|
||||
}
|
||||
case xlnt::cell::type::date:
|
||||
{
|
||||
auto typed_builder = static_cast<arrow::Date32Builder*>(builder);
|
||||
typed_builder->Append(cell.value<int>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
reader.end_worksheet();
|
||||
|
||||
auto schema = std::make_shared<arrow::Schema>(fields);
|
||||
auto arrays = std::vector<std::shared_ptr<arrow::Array>>();
|
||||
|
||||
for (size_t i = 0; i != columns.size(); ++i)
|
||||
{
|
||||
std::shared_ptr<arrow::Array> array;
|
||||
columns[i]->Finish(&array);
|
||||
arrays.emplace_back(array);
|
||||
}
|
||||
|
||||
std::shared_ptr<arrow::Table> table;
|
||||
arrow_check(MakeTable(schema, arrays, &table));
|
||||
|
||||
return arrow::py::wrap_table(table);
|
||||
}
|
||||
|
||||
PyObject *xlntpyarrow_arrow2xlsx(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static const char *keywords[] = { "table", "file", nullptr };
|
||||
static auto keywords_nc = const_cast<char **>(keywords);
|
||||
|
||||
PyObject *table = nullptr;
|
||||
PyObject *file = nullptr;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", keywords_nc, &table, &file))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!import_pyarrow())
|
||||
{
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
auto table = arrow::py::unwrap_table(pytable);
|
||||
xlnt::python_streambuf buffer(pyfile);
|
||||
std::ostream stream(&buffer);
|
||||
|
||||
xlnt::streaming_workbook_writer writer;
|
||||
writer.open(s);
|
||||
|
||||
writer.add_worksheet("Sheet1");
|
||||
|
||||
for (auto i = 0; i < table->num_columns(); ++i)
|
||||
{
|
||||
auto column_name = table->schema()->field(i)->name();
|
||||
writer.add_cell(xlnt::cell_reference(i + 1, 1)).value(column_name);
|
||||
}
|
||||
*/
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
} // extern "C"
|
@ -1,31 +0,0 @@
|
||||
// Copyright (c) 2017 Thomas Fussell
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE
|
||||
//
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
typedef struct _object PyObject;
|
||||
|
||||
PyObject *xlntpyarrow_xlsx2arrow(PyObject *self, PyObject *args, PyObject *kwargs);
|
||||
PyObject *xlntpyarrow_arrow2xlsx(PyObject *self, PyObject *args, PyObject *kwargs);
|
||||
} // extern "C"
|
@ -1,88 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/utility/typed_in_place_factory.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <Python.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
namespace xlnt {
|
||||
|
||||
/// A stream buffer getting data from and putting data into a Python file object
|
||||
/** The aims are as follow:
|
||||
|
||||
- Given a C++ function acting on a standard stream, e.g.
|
||||
|
||||
\code
|
||||
void read_inputs(std::istream& input) {
|
||||
...
|
||||
input >> something >> something_else;
|
||||
}
|
||||
\endcode
|
||||
|
||||
and given a piece of Python code which creates a file-like object,
|
||||
to be able to pass this file object to that C++ function, e.g.
|
||||
|
||||
\code
|
||||
import gzip
|
||||
gzip_file_obj = gzip.GzipFile(...)
|
||||
read_inputs(gzip_file_obj)
|
||||
\endcode
|
||||
|
||||
and have the standard stream pull data from and put data into the Python
|
||||
file object.
|
||||
|
||||
- When Python \c read_inputs() returns, the Python object is able to
|
||||
continue reading or writing where the C++ code left off.
|
||||
|
||||
- Operations in C++ on mere files should be competitively fast compared
|
||||
to the direct use of \c std::fstream.
|
||||
|
||||
|
||||
\b Motivation
|
||||
|
||||
- the standard Python library offer of file-like objects (files,
|
||||
compressed files and archives, network, ...) is far superior to the
|
||||
offer of streams in the C++ standard library and Boost C++ libraries.
|
||||
|
||||
- i/o code involves a fair amount of text processing which is more
|
||||
efficiently prototyped in Python but then one may need to rewrite
|
||||
a time-critical part in C++, in as seamless a manner as possible.
|
||||
|
||||
\b Usage
|
||||
|
||||
This is 2-step:
|
||||
|
||||
- a trivial wrapper function
|
||||
|
||||
\code
|
||||
using boost_adaptbx::python::streambuf;
|
||||
void read_inputs_wrapper(streambuf& input)
|
||||
{
|
||||
streambuf::istream is(input);
|
||||
read_inputs(is);
|
||||
}
|
||||
|
||||
def("read_inputs", read_inputs_wrapper);
|
||||
\endcode
|
||||
|
||||
which has to be written every time one wants a Python binding for
|
||||
such a C++ function.
|
||||
|
||||
- the Python side
|
||||
|
||||
\code
|
||||
from boost.python import streambuf
|
||||
read_inputs(streambuf(python_file_obj=obj, buffer_size=1024))
|
||||
\endcode
|
||||
|
||||
\c buffer_size is optional. See also: \c default_buffer_size
|
||||
|
||||
Note: references are to the C++ standard (the numbers between parentheses
|
||||
at the end of references are margin markers).
|
||||
*/
|
||||
class python_streambuf : public std::basic_streambuf<char>
|
||||
{
|
||||
private:
|
||||
@ -100,8 +24,10 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
typedef base_t::traits_type traits_type;
|
||||
|
||||
// work around Visual C++ 7.1 problem
|
||||
inline static int
|
||||
traits_type_eof() { return traits_type::eof(); }
|
||||
inline static int traits_type_eof()
|
||||
{
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
/// The default size of the read and write buffer.
|
||||
/** They are respectively used to buffer data read from and data written to
|
||||
@ -113,13 +39,13 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
/** if buffer_size is 0 the current default_buffer_size is used.
|
||||
*/
|
||||
python_streambuf(
|
||||
PyObject *python_file_obj,
|
||||
pybind11::object python_file_obj,
|
||||
std::size_t buffer_size_ = 0)
|
||||
:
|
||||
py_read (PyObject_GetAttrString(python_file_obj, "read")),
|
||||
py_write(PyObject_GetAttrString(python_file_obj, "write")),
|
||||
py_seek (PyObject_GetAttrString(python_file_obj, "seek")),
|
||||
py_tell (PyObject_GetAttrString(python_file_obj, "tell")),
|
||||
py_read(python_file_obj.attr("read").cast<pybind11::function>()),
|
||||
py_write(python_file_obj.attr("write").cast<pybind11::function>()),
|
||||
py_seek(python_file_obj.attr("seek").cast<pybind11::function>()),
|
||||
py_tell(python_file_obj.attr("tell").cast<pybind11::function>()),
|
||||
buffer_size(buffer_size_ != 0 ? buffer_size_ : default_buffer_size),
|
||||
write_buffer(0),
|
||||
pos_of_read_buffer_end_in_py_file(0),
|
||||
@ -127,34 +53,40 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
farthest_pptr(0)
|
||||
{
|
||||
assert(buffer_size != 0);
|
||||
|
||||
/* Some Python file objects (e.g. sys.stdout and sys.stdin)
|
||||
have non-functional seek and tell. If so, assign None to
|
||||
py_tell and py_seek.
|
||||
*/
|
||||
if (py_tell != nullptr) {
|
||||
PyObject_CallFunction(py_tell, nullptr);
|
||||
if (PyErr_Occurred() != nullptr)
|
||||
{
|
||||
py_tell = nullptr;
|
||||
py_seek = nullptr;
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
if (!py_tell.is_none())
|
||||
{
|
||||
try
|
||||
{
|
||||
py_tell();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
py_tell = pybind11::none();
|
||||
}
|
||||
}
|
||||
|
||||
if (py_write != nullptr) {
|
||||
if (!py_write.is_none())
|
||||
{
|
||||
// C-like string to make debugging easier
|
||||
write_buffer = new char[buffer_size + 1];
|
||||
write_buffer[buffer_size] = '\0';
|
||||
setp(write_buffer, write_buffer + buffer_size); // 27.5.2.4.5 (5)
|
||||
farthest_pptr = pptr();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// The first attempt at output will result in a call to overflow
|
||||
setp(0, 0);
|
||||
}
|
||||
|
||||
if (py_tell != nullptr) {
|
||||
auto py_pos = extract_int<off_type>(PyObject_CallFunction(py_tell, nullptr));
|
||||
if (!py_tell.is_none())
|
||||
{
|
||||
auto py_pos = py_tell().cast<pybind11::int_>();
|
||||
pos_of_read_buffer_end_in_py_file = py_pos;
|
||||
pos_of_write_buffer_end_in_py_file = py_pos;
|
||||
}
|
||||
@ -170,6 +102,7 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
member function readsome to work correctly (c.f. 27.6.1.3, alinea 30)
|
||||
*/
|
||||
virtual std::streamsize showmanyc() {
|
||||
std::cout << "showmanyc" << std::endl;
|
||||
int_type const failure = traits_type::eof();
|
||||
int_type status = underflow();
|
||||
if (status == failure) return -1;
|
||||
@ -178,15 +111,16 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
|
||||
/// C.f. C++ standard section 27.5.2.4.3
|
||||
virtual int_type underflow() {
|
||||
std::cout << "underflow" << std::endl;
|
||||
int_type const failure = traits_type::eof();
|
||||
if (py_read == nullptr) {
|
||||
if (py_read.is_none()) {
|
||||
throw std::invalid_argument(
|
||||
"That Python file object has no 'read' attribute");
|
||||
}
|
||||
read_buffer = PyObject_CallFunction(py_read, "i", buffer_size);
|
||||
read_buffer = py_read(buffer_size).cast<pybind11::bytes>();
|
||||
char *read_buffer_data = nullptr;
|
||||
Py_ssize_t py_n_read = 0;
|
||||
if (PyBytes_AsStringAndSize(read_buffer, &read_buffer_data, &py_n_read) == -1) {
|
||||
if (PyBytes_AsStringAndSize(read_buffer.ptr(), &read_buffer_data, &py_n_read) == -1) {
|
||||
setg(0, 0, 0);
|
||||
throw std::invalid_argument(
|
||||
"The method 'read' of the Python file object "
|
||||
@ -202,17 +136,18 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
|
||||
/// C.f. C++ standard section 27.5.2.4.5
|
||||
virtual int_type overflow(int_type c=traits_type_eof()) {
|
||||
if (py_write == nullptr) {
|
||||
std::cout << "overflow" << std::endl;
|
||||
if (py_write.is_none()) {
|
||||
throw std::invalid_argument(
|
||||
"That Python file object has no 'write' attribute");
|
||||
}
|
||||
farthest_pptr = std::max(farthest_pptr, pptr());
|
||||
auto n_written = (off_type)(farthest_pptr - pbase());
|
||||
auto chunk = PyBytes_FromStringAndSize(pbase(), farthest_pptr - pbase());
|
||||
PyObject_CallFunction(py_write, "O", chunk);
|
||||
py_write(chunk);
|
||||
if (!traits_type::eq_int_type(c, traits_type::eof())) {
|
||||
auto ch = traits_type::to_char_type(c);
|
||||
PyObject_CallFunction(py_write, "y#", reinterpret_cast<char *>(&ch), 1);
|
||||
auto ch = traits_type::to_char_type(c);
|
||||
py_write(reinterpret_cast<char *>(&ch), 1);
|
||||
n_written++;
|
||||
}
|
||||
if (n_written) {
|
||||
@ -233,21 +168,22 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
seek position in that read buffer.
|
||||
*/
|
||||
virtual int sync() {
|
||||
std::cout << "sync" << std::endl;
|
||||
int result = 0;
|
||||
farthest_pptr = std::max(farthest_pptr, pptr());
|
||||
if (farthest_pptr && farthest_pptr > pbase()) {
|
||||
off_type delta = pptr() - farthest_pptr;
|
||||
int_type status = overflow();
|
||||
if (traits_type::eq_int_type(status, traits_type::eof())) result = -1;
|
||||
if (py_seek != nullptr)
|
||||
if (!py_seek.is_none())
|
||||
{
|
||||
PyObject_CallFunction(py_seek, "i", delta);
|
||||
py_seek(delta);
|
||||
}
|
||||
}
|
||||
else if (gptr() && gptr() < egptr()) {
|
||||
if (py_seek != nullptr)
|
||||
if (!py_seek.is_none())
|
||||
{
|
||||
PyObject_CallFunction(py_seek, "ii", gptr() - egptr(), 1);
|
||||
py_seek(gptr() - egptr(), 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -265,6 +201,7 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
std::ios_base::openmode which= std::ios_base::in
|
||||
| std::ios_base::out)
|
||||
{
|
||||
std::cout << "seekoff" << std::endl;
|
||||
/* In practice, "which" is either std::ios_base::in or out
|
||||
since we end up here because either seekp or seekg was called
|
||||
on the stream using this buffer. That simplifies the code
|
||||
@ -272,7 +209,7 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
*/
|
||||
int const failure = off_type(-1);
|
||||
|
||||
if (py_seek == nullptr) {
|
||||
if (py_seek.is_none()) {
|
||||
throw std::invalid_argument(
|
||||
"That Python file object has no 'seek' attribute");
|
||||
}
|
||||
@ -301,20 +238,19 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
}
|
||||
|
||||
// Let's have a go
|
||||
boost::optional<off_type> result = seekoff_without_calling_python(
|
||||
off, way, which);
|
||||
if (!result) {
|
||||
auto result = seekoff_without_calling_python(off, way, which);
|
||||
if (!result.second) {
|
||||
// we need to call Python
|
||||
if (which == std::ios_base::out) overflow();
|
||||
if (way == std::ios_base::cur) {
|
||||
if (which == std::ios_base::in) off -= egptr() - gptr();
|
||||
else if (which == std::ios_base::out) off += pptr() - pbase();
|
||||
}
|
||||
PyObject_CallFunction(py_seek, "ii", off, whence);
|
||||
result = extract_int<off_type>(PyObject_CallFunction(py_tell, nullptr));
|
||||
py_seek(off, whence);
|
||||
result.first = py_tell().cast<pybind11::int_>();
|
||||
if (which == std::ios_base::in) underflow();
|
||||
}
|
||||
return *result;
|
||||
return result.first;
|
||||
}
|
||||
|
||||
/// C.f. C++ standard section 27.5.2.4.2
|
||||
@ -323,14 +259,15 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
std::ios_base::openmode which= std::ios_base::in
|
||||
| std::ios_base::out)
|
||||
{
|
||||
std::cout << "seekpos" << std::endl;
|
||||
return python_streambuf::seekoff(sp, std::ios_base::beg, which);
|
||||
}
|
||||
|
||||
private:
|
||||
PyObject *py_read = nullptr;
|
||||
PyObject *py_write = nullptr;
|
||||
PyObject *py_seek = nullptr;
|
||||
PyObject *py_tell = nullptr;
|
||||
pybind11::function py_read;
|
||||
pybind11::function py_write;
|
||||
pybind11::function py_seek;
|
||||
pybind11::function py_tell;
|
||||
|
||||
std::size_t buffer_size;
|
||||
|
||||
@ -339,7 +276,7 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
object so as to hold on it: as a result, the actual buffer can't
|
||||
go away.
|
||||
*/
|
||||
PyObject *read_buffer = nullptr;
|
||||
pybind11::bytes read_buffer;
|
||||
|
||||
/* A mere array of char's allocated on the heap at construction time and
|
||||
de-allocated only at destruction time.
|
||||
@ -353,12 +290,12 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
char *farthest_pptr = nullptr;
|
||||
|
||||
|
||||
boost::optional<off_type> seekoff_without_calling_python(
|
||||
std::pair<off_type, bool> seekoff_without_calling_python(
|
||||
off_type off,
|
||||
std::ios_base::seekdir way,
|
||||
std::ios_base::openmode which)
|
||||
{
|
||||
boost::optional<off_type> const failure;
|
||||
const auto failure = std::make_pair<off_type, bool>(off_type(), false);
|
||||
|
||||
// Buffer range and current position
|
||||
off_type buf_begin, buf_end, buf_cur, upper_bound;
|
||||
@ -403,17 +340,8 @@ class python_streambuf : public std::basic_streambuf<char>
|
||||
// we are in wonderland
|
||||
if (which == std::ios_base::in) gbump(static_cast<int>(buf_sought - buf_cur));
|
||||
else if (which == std::ios_base::out) pbump(static_cast<int>(buf_sought - buf_cur));
|
||||
return pos_of_buffer_end_in_py_file + (buf_sought - buf_end);
|
||||
return std::make_pair<off_type, bool>(pos_of_buffer_end_in_py_file + (buf_sought - buf_end), true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T extract_int(PyObject *o)
|
||||
{
|
||||
auto value = PyLong_AsLong(o);
|
||||
Py_DECREF(o);
|
||||
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t python_streambuf::default_buffer_size = 1024;
|
||||
|
@ -1,94 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
from distutils.core import setup, Extension
|
||||
from distutils import sysconfig
|
||||
|
||||
description = """
|
||||
xlntpyarrow allows Apache Arrow tables to be written to and read from an XLSX
|
||||
file efficiently using the C++ library xlnt.
|
||||
""".strip()
|
||||
|
||||
cfg_vars = sysconfig.get_config_vars()
|
||||
if 'CFLAGS' in cfg_vars:
|
||||
cfg_vars['CFLAGS'] = cfg_vars['CFLAGS'].replace('-Wstrict-prototypes', '')
|
||||
|
||||
project_root = '${CMAKE_SOURCE_DIR}'
|
||||
conda_root = '${CONDA_ROOT}'
|
||||
|
||||
xlntlib = None
|
||||
|
||||
for arg in sys.argv:
|
||||
if arg[:2] == '--' and arg.split('=')[0][2:] == 'xlntlib':
|
||||
xlntlib = arg.split('=')[1]
|
||||
sys.argv.remove(arg)
|
||||
break
|
||||
|
||||
include_dirs = [
|
||||
os.path.join(project_root, 'include'),
|
||||
os.path.join(project_root, 'source'),
|
||||
os.path.join(project_root, 'xlntpyarrow'),
|
||||
os.path.join(conda_root, 'include')
|
||||
]
|
||||
|
||||
library_dir = 'lib'
|
||||
|
||||
if os.name == 'nt':
|
||||
library_dir = 'Lib/site-packages'
|
||||
|
||||
library_dirs = [
|
||||
os.path.dirname(xlntlib),
|
||||
os.path.join(conda_root, 'lib')
|
||||
]
|
||||
|
||||
compile_args = '${CMAKE_CXX_FLAGS}'.split()
|
||||
|
||||
xlntpyarrow_extension = Extension(
|
||||
'xlntpyarrow',
|
||||
['${CMAKE_CURRENT_SOURCE_DIR}/xlntpyarrow.cpp', '${CMAKE_CURRENT_SOURCE_DIR}/methods.cpp'],
|
||||
language = 'c++',
|
||||
include_dirs = include_dirs,
|
||||
libraries = [
|
||||
'arrow',
|
||||
'arrow_python',
|
||||
'xlnt'
|
||||
],
|
||||
library_dirs = library_dirs,
|
||||
extra_compile_args = compile_args
|
||||
)
|
||||
|
||||
classifiers = [
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Plugins',
|
||||
'Intended Audience :: Science/Research',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: Microsoft :: Windows',
|
||||
'Operating System :: MacOS :: MacOS X',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: C',
|
||||
'Programming Language :: C++',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Topic :: Database',
|
||||
'Topic :: Office/Business :: Financial :: Spreadsheet',
|
||||
'Topic :: Scientific/Engineering :: Information Analysis',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
||||
]
|
||||
|
||||
data_files = []
|
||||
|
||||
if xlntlib:
|
||||
data_files.append((library_dir, [os.path.relpath(xlntlib).replace('\\', '/')]))
|
||||
|
||||
setup(
|
||||
name = 'xlntpyarrow',
|
||||
version = '1.1.0',
|
||||
classifiers = classifiers,
|
||||
description = description,
|
||||
ext_modules = [xlntpyarrow_extension],
|
||||
author = 'Thomas Fussell',
|
||||
author_email = 'thomas.fussell@gmail.com',
|
||||
url = 'https://github.com/tfussell/xlnt',
|
||||
data_files = data_files
|
||||
)
|
68
xlntpyarrow/test.py
Normal file
68
xlntpyarrow/test.py
Normal file
@ -0,0 +1,68 @@
|
||||
import pyarrow as pa
|
||||
import xlntpyarrow as xpa
|
||||
|
||||
print(xpa)
|
||||
|
||||
COLUMN_TYPE_FIELD = {
|
||||
xpa.Cell.Type.Number: pa.float64,
|
||||
xpa.Cell.Type.SharedString: pa.string,
|
||||
xpa.Cell.Type.InlineString: pa.string,
|
||||
xpa.Cell.Type.FormulaString: pa.string,
|
||||
xpa.Cell.Type.Error: pa.string,
|
||||
xpa.Cell.Type.Boolean: pa.bool_,
|
||||
xpa.Cell.Type.Date: pa.date32,
|
||||
xpa.Cell.Type.Empty: pa.string,
|
||||
}
|
||||
|
||||
def xlsx2arrow(io, sheetname):
|
||||
reader = xpa.StreamingWorkbookReader()
|
||||
reader.open(io)
|
||||
print('after open')
|
||||
|
||||
print('before titles')
|
||||
sheet_titles = reader.sheet_titles()
|
||||
print('after titles', sheet_titles)
|
||||
sheet_title = sheet_titles[0]
|
||||
|
||||
if sheetname is not None:
|
||||
if isinstance(sheetname, int):
|
||||
sheet_title = sheet_titles[sheetname]
|
||||
elif isinstance(sheetname, str):
|
||||
sheet_title = sheetname
|
||||
|
||||
print('before begin', sheet_title)
|
||||
reader.begin_worksheet(sheet_title)
|
||||
print('after begin', sheet_title)
|
||||
|
||||
column_names = []
|
||||
fields = []
|
||||
batches = []
|
||||
|
||||
while reader.has_cell():
|
||||
print('read_cell')
|
||||
cell = reader.read_cell()
|
||||
type = cell.data_type()
|
||||
|
||||
if cell.row() == 1:
|
||||
column_names.push_back(cell.value_string())
|
||||
continue
|
||||
elif cell.row() == 2:
|
||||
column_name = column_names[cell.column() - 1]
|
||||
fields.append(pa.Field(column_name, COLUMN_TYPE_FIELD[type]()))
|
||||
continue
|
||||
elif schema is None:
|
||||
schema = pa.schema(fields)
|
||||
|
||||
batch = xpa.read_batch(schema, 0)
|
||||
print(batch)
|
||||
batches.append(batch)
|
||||
|
||||
break
|
||||
|
||||
reader.end_worksheet()
|
||||
|
||||
return pa.Table.from_batches(batches)
|
||||
|
||||
if __name__ == '__main__':
|
||||
file = open('tmp.xlsx', 'rb')
|
||||
print(xlsx2arrow(file, 'Sheet1'))
|
@ -21,63 +21,328 @@
|
||||
// @license: http://www.opensource.org/licenses/mit-license.php
|
||||
// @author: see AUTHORS file
|
||||
|
||||
#include <Python.h>
|
||||
#include <methods.hpp>
|
||||
#include <arrow/api.h>
|
||||
#include <arrow/python/pyarrow.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <xlnt/xlnt.hpp>
|
||||
#include <xlnt/workbook/streaming_workbook_reader.hpp>
|
||||
#include <python_streambuf.hpp>
|
||||
|
||||
extern "C" {
|
||||
|
||||
PyDoc_STRVAR(xlntpyarrow_xlsx2arrow_doc, "xlsx2arrow(in_file)\
|
||||
\
|
||||
Returns an arrow table representing the given XLSX file object.");
|
||||
|
||||
PyDoc_STRVAR(xlntpyarrow_arrow2xlsx_doc, "arrow2xlsx(table, out_file)\
|
||||
\
|
||||
Writes the given arrow table to out_file as an XLSX file.");
|
||||
|
||||
// 2.7/3 compatible based on https://docs.python.org/3/howto/cporting.html
|
||||
|
||||
static PyMethodDef xlntpyarrow_methods[] =
|
||||
void import_pyarrow()
|
||||
{
|
||||
{ "xlsx2arrow", (PyCFunction)xlntpyarrow_xlsx2arrow,
|
||||
METH_VARARGS | METH_KEYWORDS, xlntpyarrow_xlsx2arrow_doc },
|
||||
{ "arrow2xlsx", (PyCFunction)xlntpyarrow_arrow2xlsx,
|
||||
METH_VARARGS | METH_KEYWORDS, xlntpyarrow_arrow2xlsx_doc },
|
||||
{ nullptr, nullptr, 0, nullptr }
|
||||
};
|
||||
static auto imported = false;
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if (!imported)
|
||||
{
|
||||
if (arrow::py::import_pyarrow() != 0)
|
||||
{
|
||||
throw std::exception("Import of pyarrow failed.");
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(xlntpyarrow_doc, "The xlntpyarrow module");
|
||||
|
||||
static PyModuleDef xlntpyarrow_def =
|
||||
{
|
||||
PyModuleDef_HEAD_INIT, // m_base
|
||||
"xlntpyarrow", // m_name
|
||||
xlntpyarrow_doc, // m_doc
|
||||
0, // m_size
|
||||
xlntpyarrow_methods, // m_methods
|
||||
nullptr, // m_slots
|
||||
nullptr, // m_traverse
|
||||
nullptr, // m_clear
|
||||
nullptr, // m_free
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_xlntpyarrow(void)
|
||||
#else
|
||||
void
|
||||
initxlntpyarrow(void)
|
||||
#endif
|
||||
{
|
||||
PyObject *module = nullptr;
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
module = PyModule_Create(&xlntpyarrow_def);
|
||||
return module;
|
||||
#else
|
||||
module = Py_InitModule("xlntpyarrow", xlntpyarrow_methods);
|
||||
return;
|
||||
#endif
|
||||
imported = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
std::vector<arrow::Type::type> extract_schema_types(std::shared_ptr<arrow::Schema> &schema)
|
||||
{
|
||||
auto types = std::vector<arrow::Type::type>();
|
||||
|
||||
for (auto i = 0; i < schema->num_fields(); ++i)
|
||||
{
|
||||
types.push_back(schema->field(i)->type()->id());
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
std::unique_ptr<arrow::ArrayBuilder> make_array_builder(arrow::Type::type type)
|
||||
{
|
||||
std::unique_ptr<arrow::ArrayBuilder> builder;
|
||||
auto pool = arrow::default_memory_pool();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case arrow::Type::NA:
|
||||
break;
|
||||
|
||||
case arrow::Type::BOOL:
|
||||
builder.reset(new arrow::BooleanBuilder(pool));
|
||||
break;
|
||||
|
||||
case arrow::Type::UINT8:
|
||||
break;
|
||||
|
||||
case arrow::Type::INT8:
|
||||
break;
|
||||
|
||||
case arrow::Type::UINT16:
|
||||
break;
|
||||
|
||||
case arrow::Type::INT16:
|
||||
break;
|
||||
|
||||
case arrow::Type::UINT32:
|
||||
break;
|
||||
|
||||
case arrow::Type::INT32:
|
||||
break;
|
||||
|
||||
case arrow::Type::UINT64:
|
||||
break;
|
||||
|
||||
case arrow::Type::INT64:
|
||||
break;
|
||||
|
||||
case arrow::Type::HALF_FLOAT:
|
||||
break;
|
||||
|
||||
case arrow::Type::FLOAT:
|
||||
break;
|
||||
|
||||
case arrow::Type::DOUBLE:
|
||||
builder.reset(new arrow::DoubleBuilder(pool));
|
||||
break;
|
||||
|
||||
case arrow::Type::STRING:
|
||||
builder.reset(new arrow::StringBuilder(pool));
|
||||
break;
|
||||
|
||||
case arrow::Type::BINARY:
|
||||
break;
|
||||
|
||||
case arrow::Type::FIXED_SIZE_BINARY:
|
||||
break;
|
||||
|
||||
case arrow::Type::DATE32:
|
||||
builder.reset(new arrow::Date32Builder(pool));
|
||||
break;
|
||||
|
||||
case arrow::Type::DATE64:
|
||||
break;
|
||||
|
||||
case arrow::Type::TIMESTAMP:
|
||||
break;
|
||||
|
||||
case arrow::Type::TIME32:
|
||||
break;
|
||||
|
||||
case arrow::Type::TIME64:
|
||||
break;
|
||||
|
||||
case arrow::Type::INTERVAL:
|
||||
break;
|
||||
|
||||
case arrow::Type::DECIMAL:
|
||||
break;
|
||||
|
||||
case arrow::Type::LIST:
|
||||
break;
|
||||
|
||||
case arrow::Type::STRUCT:
|
||||
break;
|
||||
|
||||
case arrow::Type::UNION:
|
||||
break;
|
||||
|
||||
case arrow::Type::DICTIONARY:
|
||||
break;
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
void open_file(xlnt::streaming_workbook_reader &reader, pybind11::object file)
|
||||
{
|
||||
xlnt::python_streambuf buffer(file);
|
||||
std::istream stream(&buffer);
|
||||
reader.open(stream);
|
||||
}
|
||||
|
||||
pybind11::handle read_batch(xlnt::streaming_workbook_reader &reader,
|
||||
pybind11::object pyschema, int max_rows)
|
||||
{
|
||||
import_pyarrow();
|
||||
|
||||
std::shared_ptr<arrow::Schema> schema;
|
||||
arrow::py::unwrap_schema(pyschema.ptr(), &schema);
|
||||
|
||||
std::cout << "1" << std::endl;
|
||||
|
||||
auto column_types = extract_schema_types(schema);
|
||||
auto builders = std::vector<std::shared_ptr<arrow::ArrayBuilder>>();
|
||||
auto num_rows = std::int64_t(0);
|
||||
|
||||
std::cout << "2" << std::endl;
|
||||
|
||||
for (auto type : column_types)
|
||||
{
|
||||
builders.push_back(make_array_builder(type));
|
||||
}
|
||||
|
||||
std::cout << "3" << std::endl;
|
||||
|
||||
for (auto row = 0; row < max_rows; ++row)
|
||||
{
|
||||
if (!reader.has_cell()) break;
|
||||
|
||||
std::cout << "4" << std::endl;
|
||||
|
||||
for (auto column = 0; column < schema->num_fields(); ++column)
|
||||
{
|
||||
if (!reader.has_cell()) break;
|
||||
|
||||
std::cout << "5" << std::endl;
|
||||
|
||||
auto cell = reader.read_cell();
|
||||
|
||||
/*
|
||||
auto column_type = column_types.at(column);
|
||||
auto builder = builders.at(cell.column().index - 1).get();
|
||||
|
||||
switch (column_type)
|
||||
{
|
||||
case arrow::Type::NA:
|
||||
break;
|
||||
|
||||
case arrow::Type::BOOL:
|
||||
static_cast<arrow::BooleanBuilder *>(builder)->Append(cell.value<bool>());
|
||||
break;
|
||||
|
||||
case arrow::Type::UINT8:
|
||||
break;
|
||||
|
||||
case arrow::Type::INT8:
|
||||
break;
|
||||
|
||||
case arrow::Type::UINT16:
|
||||
break;
|
||||
|
||||
case arrow::Type::INT16:
|
||||
break;
|
||||
|
||||
case arrow::Type::UINT32:
|
||||
break;
|
||||
|
||||
case arrow::Type::INT32:
|
||||
break;
|
||||
|
||||
case arrow::Type::UINT64:
|
||||
break;
|
||||
|
||||
case arrow::Type::INT64:
|
||||
break;
|
||||
|
||||
case arrow::Type::HALF_FLOAT:
|
||||
break;
|
||||
|
||||
case arrow::Type::FLOAT:
|
||||
break;
|
||||
|
||||
case arrow::Type::DOUBLE:
|
||||
static_cast<arrow::DoubleBuilder *>(builder)->Append(cell.value<long double>());
|
||||
break;
|
||||
|
||||
case arrow::Type::STRING:
|
||||
static_cast<arrow::StringBuilder *>(builder)->Append(cell.value<std::string>());
|
||||
break;
|
||||
|
||||
case arrow::Type::BINARY:
|
||||
break;
|
||||
|
||||
case arrow::Type::FIXED_SIZE_BINARY:
|
||||
break;
|
||||
|
||||
case arrow::Type::DATE32:
|
||||
static_cast<arrow::Date32Builder *>(builder)->Append(cell.value<int>());
|
||||
break;
|
||||
|
||||
case arrow::Type::DATE64:
|
||||
break;
|
||||
|
||||
case arrow::Type::TIMESTAMP:
|
||||
break;
|
||||
|
||||
case arrow::Type::TIME32:
|
||||
break;
|
||||
|
||||
case arrow::Type::TIME64:
|
||||
break;
|
||||
|
||||
case arrow::Type::INTERVAL:
|
||||
break;
|
||||
|
||||
case arrow::Type::DECIMAL:
|
||||
break;
|
||||
|
||||
case arrow::Type::LIST:
|
||||
break;
|
||||
|
||||
case arrow::Type::STRUCT:
|
||||
break;
|
||||
|
||||
case arrow::Type::UNION:
|
||||
break;
|
||||
|
||||
case arrow::Type::DICTIONARY:
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
++num_rows;
|
||||
}
|
||||
|
||||
std::cout << "6" << std::endl;
|
||||
|
||||
auto columns = std::vector<std::shared_ptr<arrow::Array>>();
|
||||
|
||||
for (auto &builder : builders)
|
||||
{
|
||||
std::shared_ptr<arrow::Array> column;
|
||||
builder->Finish(&column);
|
||||
columns.emplace_back(column);
|
||||
}
|
||||
|
||||
std::cout << "7" << std::endl;
|
||||
|
||||
auto batch_pointer = std::make_shared<arrow::RecordBatch>(schema, num_rows, columns);
|
||||
auto batch_object = arrow::py::wrap_record_batch(batch_pointer);
|
||||
auto batch_handle = pybind11::handle(batch_object); // don't need to incr. reference count, right?
|
||||
|
||||
std::cout << "8" << std::endl;
|
||||
|
||||
return batch_handle;
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(xlntpyarrow, m)
|
||||
{
|
||||
m.doc() = "streaming read/write interface for C++ XLSX library xlnt";
|
||||
|
||||
pybind11::class_<xlnt::streaming_workbook_reader>(m, "StreamingWorkbookReader")
|
||||
.def(pybind11::init<>())
|
||||
.def("has_cell", &xlnt::streaming_workbook_reader::has_cell)
|
||||
.def("read_cell", &xlnt::streaming_workbook_reader::read_cell)
|
||||
.def("has_worksheet", &xlnt::streaming_workbook_reader::has_worksheet)
|
||||
.def("begin_worksheet", &xlnt::streaming_workbook_reader::begin_worksheet)
|
||||
.def("end_worksheet", &xlnt::streaming_workbook_reader::end_worksheet)
|
||||
.def("sheet_titles", &xlnt::streaming_workbook_reader::sheet_titles)
|
||||
.def("open", &open_file)
|
||||
.def("read_batch", &read_batch);
|
||||
|
||||
pybind11::class_<xlnt::cell> cell(m, "Cell");
|
||||
cell.def("value_string", [](xlnt::cell cell)
|
||||
{
|
||||
return cell.value<std::string>();
|
||||
});
|
||||
|
||||
pybind11::enum_<xlnt::cell::type>(cell, "Type")
|
||||
.value("Empty", xlnt::cell::type::empty)
|
||||
.value("Boolean", xlnt::cell::type::boolean)
|
||||
.value("Date", xlnt::cell::type::date)
|
||||
.value("Error", xlnt::cell::type::error)
|
||||
.value("InlineString", xlnt::cell::type::inline_string)
|
||||
.value("Number", xlnt::cell::type::number)
|
||||
.value("SharedString", xlnt::cell::type::shared_string)
|
||||
.value("FormulaString", xlnt::cell::type::formula_string);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user