diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 179be4ce6..c85a3a08a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,6 +66,24 @@ jobs: # Inject a couple Windows 2019 runs - runs-on: windows-2019 python: '3.9' + # Inject a few runs with different runtime libraries + - runs-on: windows-2022 + python: '3.9' + args: > + -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded + - runs-on: windows-2022 + python: '3.10' + args: > + -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL + # This needs a python built with MTd + # - runs-on: windows-2022 + # python: '3.11' + # args: > + # -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug + - runs-on: windows-2022 + python: '3.12' + args: > + -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL # Extra ubuntu latest job - runs-on: ubuntu-latest python: '3.11' diff --git a/CMakeLists.txt b/CMakeLists.txt index 281440827..d8323c10f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,9 @@ set(PYBIND11_HEADERS include/pybind11/chrono.h include/pybind11/common.h include/pybind11/complex.h + include/pybind11/conduit/pybind11_conduit_v1.h + include/pybind11/conduit/pybind11_platform_abi_id.h + include/pybind11/conduit/wrap_include_python_h.h include/pybind11/options.h include/pybind11/eigen.h include/pybind11/eigen/common.h diff --git a/include/pybind11/conduit/README.txt b/include/pybind11/conduit/README.txt new file mode 100644 index 000000000..9a2c53ba4 --- /dev/null +++ b/include/pybind11/conduit/README.txt @@ -0,0 +1,15 @@ +NOTE +---- + +The C++ code here + +** only depends on ** + +and nothing else. + +DO NOT ADD CODE WITH OTHER EXTERNAL DEPENDENCIES TO THIS DIRECTORY. + +Read on: + +pybind11_conduit_v1.h — Type-safe interoperability between different + independent Python/C++ bindings systems. diff --git a/include/pybind11/conduit/pybind11_conduit_v1.h b/include/pybind11/conduit/pybind11_conduit_v1.h new file mode 100644 index 000000000..e3a453453 --- /dev/null +++ b/include/pybind11/conduit/pybind11_conduit_v1.h @@ -0,0 +1,111 @@ +// Copyright (c) 2024 The pybind Community. + +/* The pybind11_conduit_v1 feature enables type-safe interoperability between + +* different independent Python/C++ bindings systems, + +* including pybind11 versions with different PYBIND11_INTERNALS_VERSION's. + +The naming of the feature is a bit misleading: + +* The feature is in no way tied to pybind11 internals. + +* It just happens to originate from pybind11 and currently still lives there. + +* The only external dependency is . + +The implementation is a VERY light-weight dependency. It is designed to be +compatible with any ISO C++11 (or higher) compiler, and does NOT require +C++ Exception Handling to be enabled. + +Please see https://github.com/pybind/pybind11/pull/5296 for more background. + +The implementation involves a + +def _pybind11_conduit_v1_( + self, + pybind11_platform_abi_id: bytes, + cpp_type_info_capsule: capsule, + pointer_kind: bytes) -> capsule + +method that is meant to be added to Python objects wrapping C++ objects +(e.g. pybind11::class_-wrapped types). + +The design of the _pybind11_conduit_v1_ feature provides two layers of +protection against C++ ABI mismatches: + +* The first and most important layer is that the pybind11_platform_abi_id's + must match between extensions. — This will never be perfect, but is the same + pragmatic approach used in pybind11 since 2017 + (https://github.com/pybind/pybind11/commit/96997a4b9d4ec3d389a570604394af5d5eee2557, + PYBIND11_INTERNALS_ID). + +* The second layer is that the typeid(std::type_info).name()'s must match + between extensions. + +The implementation below (which is shorter than this comment!), serves as a +battle-tested specification. The main API is this one function: + +auto *cpp_pointer = pybind11_conduit_v1::get_type_pointer_ephemeral(py_obj); + +It is meant to be a minimalistic reference implementation, intentionally +without comprehensive error reporting. It is expected that major bindings +systems will roll their own, compatible implementations, potentially with +system-specific error reporting. The essential specifications all bindings +systems need to agree on are merely: + +* PYBIND11_PLATFORM_ABI_ID (const char* literal). + +* The cpp_type_info capsule (see below: a void *ptr and a const char *name). + +* The cpp_conduit capsule (see below: a void *ptr and a const char *name). + +* "raw_pointer_ephemeral" means: the lifetime of the pointer is the lifetime + of the py_obj. + +*/ + +// THIS MUST STAY AT THE TOP! +#include "pybind11_platform_abi_id.h" + +#include +#include + +namespace pybind11_conduit_v1 { + +inline void *get_raw_pointer_ephemeral(PyObject *py_obj, const std::type_info *cpp_type_info) { + PyObject *cpp_type_info_capsule + = PyCapsule_New(const_cast(static_cast(cpp_type_info)), + typeid(std::type_info).name(), + nullptr); + if (cpp_type_info_capsule == nullptr) { + return nullptr; + } + PyObject *cpp_conduit = PyObject_CallMethod(py_obj, + "_pybind11_conduit_v1_", + "yOy", + PYBIND11_PLATFORM_ABI_ID, + cpp_type_info_capsule, + "raw_pointer_ephemeral"); + Py_DECREF(cpp_type_info_capsule); + if (cpp_conduit == nullptr) { + return nullptr; + } + void *raw_ptr = PyCapsule_GetPointer(cpp_conduit, cpp_type_info->name()); + Py_DECREF(cpp_conduit); + if (PyErr_Occurred()) { + return nullptr; + } + return raw_ptr; +} + +template +T *get_type_pointer_ephemeral(PyObject *py_obj) { + void *raw_ptr = get_raw_pointer_ephemeral(py_obj, &typeid(T)); + if (raw_ptr == nullptr) { + return nullptr; + } + return static_cast(raw_ptr); +} + +} // namespace pybind11_conduit_v1 diff --git a/include/pybind11/conduit/pybind11_platform_abi_id.h b/include/pybind11/conduit/pybind11_platform_abi_id.h new file mode 100644 index 000000000..a7733bcf5 --- /dev/null +++ b/include/pybind11/conduit/pybind11_platform_abi_id.h @@ -0,0 +1,88 @@ +#pragma once + +// Copyright (c) 2024 The pybind Community. + +// To maximize reusability: +// DO NOT ADD CODE THAT REQUIRES C++ EXCEPTION HANDLING. + +#include "wrap_include_python_h.h" + +// Implementation details. DO NOT USE ELSEWHERE. (Unfortunately we cannot #undef them.) +// This is duplicated here to maximize portability. +#define PYBIND11_PLATFORM_ABI_ID_STRINGIFY(x) #x +#define PYBIND11_PLATFORM_ABI_ID_TOSTRING(x) PYBIND11_PLATFORM_ABI_ID_STRINGIFY(x) + +// On MSVC, debug and release builds are not ABI-compatible! +#if defined(_MSC_VER) && defined(_DEBUG) +# define PYBIND11_BUILD_TYPE "_debug" +#else +# define PYBIND11_BUILD_TYPE "" +#endif + +// Let's assume that different compilers are ABI-incompatible. +// A user can manually set this string if they know their +// compiler is compatible. +#ifndef PYBIND11_COMPILER_TYPE +# if defined(_MSC_VER) +# define PYBIND11_COMPILER_TYPE "_msvc" +# elif defined(__INTEL_COMPILER) +# define PYBIND11_COMPILER_TYPE "_icc" +# elif defined(__clang__) +# define PYBIND11_COMPILER_TYPE "_clang" +# elif defined(__PGI) +# define PYBIND11_COMPILER_TYPE "_pgi" +# elif defined(__MINGW32__) +# define PYBIND11_COMPILER_TYPE "_mingw" +# elif defined(__CYGWIN__) +# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" +# elif defined(__GNUC__) +# define PYBIND11_COMPILER_TYPE "_gcc" +# else +# define PYBIND11_COMPILER_TYPE "_unknown" +# endif +#endif + +// Also standard libs +#ifndef PYBIND11_STDLIB +# if defined(_LIBCPP_VERSION) +# define PYBIND11_STDLIB "_libcpp" +# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) +# define PYBIND11_STDLIB "_libstdcpp" +# else +# define PYBIND11_STDLIB "" +# endif +#endif + +#ifndef PYBIND11_BUILD_ABI +# if defined(__GXX_ABI_VERSION) // Linux/OSX. +# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_PLATFORM_ABI_ID_TOSTRING(__GXX_ABI_VERSION) +# elif defined(_MSC_VER) // See PR #4953. +# if defined(_MT) && defined(_DLL) // Corresponding to CL command line options /MD or /MDd. +# if (_MSC_VER) / 100 == 19 +# define PYBIND11_BUILD_ABI "_md_mscver19" +# else +# error "Unknown major version for MSC_VER: PLEASE REVISE THIS CODE." +# endif +# elif defined(_MT) // Corresponding to CL command line options /MT or /MTd. +# define PYBIND11_BUILD_ABI "_mt_mscver" PYBIND11_PLATFORM_ABI_ID_TOSTRING(_MSC_VER) +# else +# if (_MSC_VER) / 100 == 19 +# define PYBIND11_BUILD_ABI "_none_mscver19" +# else +# error "Unknown major version for MSC_VER: PLEASE REVISE THIS CODE." +# endif +# endif +# elif defined(__NVCOMPILER) // NVHPC (PGI-based). +# define PYBIND11_BUILD_ABI "" // TODO: What should be here, to prevent UB? +# else +# error "Unknown platform or compiler: PLEASE REVISE THIS CODE." +# endif +#endif + +#ifndef PYBIND11_INTERNALS_KIND +# define PYBIND11_INTERNALS_KIND "" +#endif + +#define PYBIND11_PLATFORM_ABI_ID \ + PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ + PYBIND11_BUILD_TYPE diff --git a/include/pybind11/conduit/wrap_include_python_h.h b/include/pybind11/conduit/wrap_include_python_h.h new file mode 100644 index 000000000..316d1afc8 --- /dev/null +++ b/include/pybind11/conduit/wrap_include_python_h.h @@ -0,0 +1,72 @@ +#pragma once + +// Copyright (c) 2024 The pybind Community. + +// STRONG REQUIREMENT: +// This header is a wrapper around `#include `, therefore it +// MUST BE INCLUDED BEFORE ANY STANDARD HEADERS are included. +// See also: +// https://docs.python.org/3/c-api/intro.html#include-files +// Quoting from there: +// Note: Since Python may define some pre-processor definitions which affect +// the standard headers on some systems, you must include Python.h before +// any standard headers are included. + +// To maximize reusability: +// DO NOT ADD CODE THAT REQUIRES C++ EXCEPTION HANDLING. + +// Disable linking to pythonX_d.lib on Windows in debug mode. +#if defined(_MSC_VER) && defined(_DEBUG) && !defined(Py_DEBUG) +// Workaround for a VS 2022 issue. +// See https://github.com/pybind/pybind11/pull/3497 for full context. +// NOTE: This workaround knowingly violates the Python.h include order +// requirement (see above). +# include +# if _MSVC_STL_VERSION >= 143 +# include +# endif +# define PYBIND11_DEBUG_MARKER +# undef _DEBUG +#endif + +// Don't let Python.h #define (v)snprintf as macro because they are implemented +// properly in Visual Studio since 2015. +#if defined(_MSC_VER) +# define HAVE_SNPRINTF 1 +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4505) +// C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed +#endif + +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#if defined(PYBIND11_DEBUG_MARKER) +# define _DEBUG +# undef PYBIND11_DEBUG_MARKER +#endif + +// Python #defines overrides on all sorts of core functions, which +// tends to wreak havok in C++ codebases that expect these to work +// like regular functions (potentially with several overloads). +#if defined(isalnum) +# undef isalnum +# undef isalpha +# undef islower +# undef isspace +# undef isupper +# undef tolower +# undef toupper +#endif + +#if defined(copysign) +# undef copysign +#endif diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index 643010a0f..c13732df7 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -585,9 +585,9 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla return -1; } std::memset(view, 0, sizeof(Py_buffer)); - buffer_info *info = nullptr; + std::unique_ptr info = nullptr; try { - info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + info.reset(tinfo->get_buffer(obj, tinfo->get_buffer_data)); } catch (...) { try_translate_exceptions(); raise_from(PyExc_BufferError, "Error getting buffer"); @@ -598,7 +598,6 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla } if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) { - delete info; // view->obj = nullptr; // Was just memset to 0, so not necessary set_error(PyExc_BufferError, "Writable buffer requested for readonly storage"); return -1; @@ -606,9 +605,6 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla // Fill in all the information, and then downgrade as requested by the caller, or raise an // error if that's not possible. - view->obj = obj; - view->internal = info; - view->buf = info->ptr; view->itemsize = info->itemsize; view->len = view->itemsize; for (auto s : info->shape) { @@ -626,7 +622,6 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla if ((flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS) { if (PyBuffer_IsContiguous(view, 'C') == 0) { std::memset(view, 0, sizeof(Py_buffer)); - delete info; set_error(PyExc_BufferError, "C-contiguous buffer requested for discontiguous storage"); return -1; @@ -634,7 +629,6 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla } else if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS) { if (PyBuffer_IsContiguous(view, 'F') == 0) { std::memset(view, 0, sizeof(Py_buffer)); - delete info; set_error(PyExc_BufferError, "Fortran-contiguous buffer requested for discontiguous storage"); return -1; @@ -642,7 +636,6 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla } else if ((flags & PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS) { if (PyBuffer_IsContiguous(view, 'A') == 0) { std::memset(view, 0, sizeof(Py_buffer)); - delete info; set_error(PyExc_BufferError, "Contiguous buffer requested for discontiguous storage"); return -1; } @@ -652,7 +645,6 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla // https://docs.python.org/3/c-api/buffer.html#contiguity-requests if (PyBuffer_IsContiguous(view, 'C') == 0) { std::memset(view, 0, sizeof(Py_buffer)); - delete info; set_error(PyExc_BufferError, "C-contiguous buffer requested for discontiguous storage"); return -1; @@ -667,6 +659,11 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla } } + // Set these after all checks so they don't leak out into the caller, and can be automatically + // cleaned up on error. + view->buf = info->ptr; + view->internal = info.release(); + view->obj = obj; Py_INCREF(view->obj); return 0; } diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 3e640b206..8913d6f72 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -9,6 +9,11 @@ #pragma once +#include +#if PY_VERSION_HEX < 0x03080000 +# error "PYTHON < 3.8 IS UNSUPPORTED. pybind11 v2.13 was the last to support Python 3.7." +#endif + #define PYBIND11_VERSION_MAJOR 3 #define PYBIND11_VERSION_MINOR 0 #define PYBIND11_VERSION_PATCH 0.dev1 @@ -204,31 +209,6 @@ # define PYBIND11_MAYBE_UNUSED __attribute__((__unused__)) #endif -/* Don't let Python.h #define (v)snprintf as macro because they are implemented - properly in Visual Studio since 2015. */ -#if defined(_MSC_VER) -# define HAVE_SNPRINTF 1 -#endif - -/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode -#if defined(_MSC_VER) -PYBIND11_WARNING_PUSH -PYBIND11_WARNING_DISABLE_MSVC(4505) -// C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only) -# if defined(_DEBUG) && !defined(Py_DEBUG) -// Workaround for a VS 2022 issue. -// NOTE: This workaround knowingly violates the Python.h include order requirement: -// https://docs.python.org/3/c-api/intro.html#include-files -// See https://github.com/pybind/pybind11/pull/3497 for full context. -# include -# if _MSVC_STL_VERSION >= 143 -# include -# endif -# define PYBIND11_DEBUG_MARKER -# undef _DEBUG -# endif -#endif - // https://en.cppreference.com/w/c/chrono/localtime #if defined(__STDC_LIB_EXT1__) && !defined(__STDC_WANT_LIB_EXT1__) # define __STDC_WANT_LIB_EXT1__ @@ -263,30 +243,6 @@ PYBIND11_WARNING_DISABLE_MSVC(4505) # endif #endif -#include -#if PY_VERSION_HEX < 0x03080000 -# error "PYTHON < 3.8 IS UNSUPPORTED. pybind11 v2.13 was the last to support Python 3.7." -#endif -#include -#include - -/* Python #defines overrides on all sorts of core functions, which - tends to weak havok in C++ codebases that expect these to work - like regular functions (potentially with several overloads) */ -#if defined(isalnum) -# undef isalnum -# undef isalpha -# undef islower -# undef isspace -# undef isupper -# undef tolower -# undef toupper -#endif - -#if defined(copysign) -# undef copysign -#endif - #if defined(PYBIND11_NUMPY_1_ONLY) # define PYBIND11_INTERNAL_NUMPY_1_ONLY_DETECTED #endif @@ -295,14 +251,6 @@ PYBIND11_WARNING_DISABLE_MSVC(4505) # define PYBIND11_SIMPLE_GIL_MANAGEMENT #endif -#if defined(_MSC_VER) -# if defined(PYBIND11_DEBUG_MARKER) -# define _DEBUG -# undef PYBIND11_DEBUG_MARKER -# endif -PYBIND11_WARNING_POP -#endif - #include #include #include diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 0843d8273..87580dbd9 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -15,6 +15,7 @@ # include #endif +#include #include #include @@ -295,67 +296,6 @@ struct type_info { #endif }; -/// On MSVC, debug and release builds are not ABI-compatible! -#if defined(_MSC_VER) && defined(_DEBUG) -# define PYBIND11_BUILD_TYPE "_debug" -#else -# define PYBIND11_BUILD_TYPE "" -#endif - -/// Let's assume that different compilers are ABI-incompatible. -/// A user can manually set this string if they know their -/// compiler is compatible. -#ifndef PYBIND11_COMPILER_TYPE -# if defined(_MSC_VER) -# define PYBIND11_COMPILER_TYPE "_msvc" -# elif defined(__INTEL_COMPILER) -# define PYBIND11_COMPILER_TYPE "_icc" -# elif defined(__clang__) -# define PYBIND11_COMPILER_TYPE "_clang" -# elif defined(__PGI) -# define PYBIND11_COMPILER_TYPE "_pgi" -# elif defined(__MINGW32__) -# define PYBIND11_COMPILER_TYPE "_mingw" -# elif defined(__CYGWIN__) -# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" -# elif defined(__GNUC__) -# define PYBIND11_COMPILER_TYPE "_gcc" -# else -# define PYBIND11_COMPILER_TYPE "_unknown" -# endif -#endif - -/// Also standard libs -#ifndef PYBIND11_STDLIB -# if defined(_LIBCPP_VERSION) -# define PYBIND11_STDLIB "_libcpp" -# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) -# define PYBIND11_STDLIB "_libstdcpp" -# else -# define PYBIND11_STDLIB "" -# endif -#endif - -/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. -/// On MSVC, changes in _MSC_VER may indicate ABI incompatibility (#2898). -#ifndef PYBIND11_BUILD_ABI -# if defined(__GXX_ABI_VERSION) -# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) -# elif defined(_MSC_VER) -# define PYBIND11_BUILD_ABI "_mscver" PYBIND11_TOSTRING(_MSC_VER) -# else -# define PYBIND11_BUILD_ABI "" -# endif -#endif - -#ifndef PYBIND11_INTERNALS_KIND -# define PYBIND11_INTERNALS_KIND "" -#endif - -#define PYBIND11_PLATFORM_ABI_ID \ - PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ - PYBIND11_BUILD_TYPE - #define PYBIND11_INTERNALS_ID \ "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ PYBIND11_PLATFORM_ABI_ID "__" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index aa6a5bfd6..5e6ab15d4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -508,6 +508,12 @@ foreach(target ${test_targets}) if(SKBUILD) install(TARGETS ${target} LIBRARY DESTINATION .) endif() + + if("${target}" STREQUAL "exo_planet_c_api") + if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang|NVHPC)") + target_compile_options(${target} PRIVATE -fno-exceptions) + endif() + endif() endforeach() # Provide nice organisation in IDEs diff --git a/tests/exo_planet_c_api.cpp b/tests/exo_planet_c_api.cpp index 3bde0b27b..151010717 100644 --- a/tests/exo_planet_c_api.cpp +++ b/tests/exo_planet_c_api.cpp @@ -1,59 +1,33 @@ // Copyright (c) 2024 The pybind Community. +// In production situations it is totally fine to build with +// C++ Exception Handling enabled. However, here we want to ensure that +// C++ Exception Handling is not required. +#if defined(_MSC_VER) || defined(__EMSCRIPTEN__) +// Too much trouble making the required cmake changes (see PR #5375). +#else +# ifdef __cpp_exceptions +// https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations#__cpp_exceptions +# error This test is meant to be built with C++ Exception Handling disabled, but __cpp_exceptions is defined. +# endif +# ifdef __EXCEPTIONS +// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html +# error This test is meant to be built with C++ Exception Handling disabled, but __EXCEPTIONS is defined. +# endif +#endif + // THIS MUST STAY AT THE TOP! -#include // EXCLUSIVELY for PYBIND11_PLATFORM_ABI_ID -// Potential future direction to maximize reusability: -// (e.g. for use from SWIG, Cython, PyCLIF, nanobind): -// #include -// This would only depend on: -// 1. A C++ compiler, WITHOUT requiring -fexceptions. -// 2. Python.h +#include // VERY light-weight dependency. #include "test_cpp_conduit_traveler_types.h" #include -#include namespace { -void *get_cpp_conduit_void_ptr(PyObject *py_obj, const std::type_info *cpp_type_info) { - PyObject *cpp_type_info_capsule - = PyCapsule_New(const_cast(static_cast(cpp_type_info)), - typeid(std::type_info).name(), - nullptr); - if (cpp_type_info_capsule == nullptr) { - return nullptr; - } - PyObject *cpp_conduit = PyObject_CallMethod(py_obj, - "_pybind11_conduit_v1_", - "yOy", - PYBIND11_PLATFORM_ABI_ID, - cpp_type_info_capsule, - "raw_pointer_ephemeral"); - Py_DECREF(cpp_type_info_capsule); - if (cpp_conduit == nullptr) { - return nullptr; - } - void *void_ptr = PyCapsule_GetPointer(cpp_conduit, cpp_type_info->name()); - Py_DECREF(cpp_conduit); - if (PyErr_Occurred()) { - return nullptr; - } - return void_ptr; -} - -template -T *get_cpp_conduit_type_ptr(PyObject *py_obj) { - void *void_ptr = get_cpp_conduit_void_ptr(py_obj, &typeid(T)); - if (void_ptr == nullptr) { - return nullptr; - } - return static_cast(void_ptr); -} - extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) { - const auto *cpp_traveler - = get_cpp_conduit_type_ptr(traveler); + const auto *cpp_traveler = pybind11_conduit_v1::get_type_pointer_ephemeral< + pybind11_tests::test_cpp_conduit::Traveler>(traveler); if (cpp_traveler == nullptr) { return nullptr; } @@ -61,9 +35,8 @@ extern "C" PyObject *wrapGetLuggage(PyObject * /*self*/, PyObject *traveler) { } extern "C" PyObject *wrapGetPoints(PyObject * /*self*/, PyObject *premium_traveler) { - const auto *cpp_premium_traveler - = get_cpp_conduit_type_ptr( - premium_traveler); + const auto *cpp_premium_traveler = pybind11_conduit_v1::get_type_pointer_ephemeral< + pybind11_tests::test_cpp_conduit::PremiumTraveler>(premium_traveler); if (cpp_premium_traveler == nullptr) { return nullptr; } diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index 3c08ebfb4..f50fb1530 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -52,6 +52,13 @@ main_headers = { "include/pybind11/warnings.h", } +conduit_headers = { + "include/pybind11/conduit/README.txt", + "include/pybind11/conduit/pybind11_conduit_v1.h", + "include/pybind11/conduit/pybind11_platform_abi_id.h", + "include/pybind11/conduit/wrap_include_python_h.h", +} + detail_headers = { "include/pybind11/detail/class.h", "include/pybind11/detail/common.h", @@ -102,7 +109,7 @@ py_files = { "setup_helpers.py", } -headers = main_headers | detail_headers | eigen_headers | stl_headers +headers = main_headers | conduit_headers | detail_headers | eigen_headers | stl_headers src_files = headers | cmake_files | pkgconfig_files all_files = src_files | py_files @@ -111,6 +118,7 @@ sdist_files = { "pybind11", "pybind11/include", "pybind11/include/pybind11", + "pybind11/include/pybind11/conduit", "pybind11/include/pybind11/detail", "pybind11/include/pybind11/eigen", "pybind11/include/pybind11/stl", diff --git a/tools/setup_global.py.in b/tools/setup_global.py.in index 885ac5c72..99b8a2b29 100644 --- a/tools/setup_global.py.in +++ b/tools/setup_global.py.in @@ -26,12 +26,14 @@ class InstallHeadersNested(install_headers): main_headers = glob.glob("pybind11/include/pybind11/*.h") +conduit_headers = sum([glob.glob(f"pybind11/include/pybind11/conduit/*.{ext}") + for ext in ("h", "txt")], []) detail_headers = glob.glob("pybind11/include/pybind11/detail/*.h") eigen_headers = glob.glob("pybind11/include/pybind11/eigen/*.h") stl_headers = glob.glob("pybind11/include/pybind11/stl/*.h") cmake_files = glob.glob("pybind11/share/cmake/pybind11/*.cmake") pkgconfig_files = glob.glob("pybind11/share/pkgconfig/*.pc") -headers = main_headers + detail_headers + stl_headers + eigen_headers +headers = main_headers + conduit_headers + detail_headers + eigen_headers + stl_headers cmdclass = {"install_headers": InstallHeadersNested} $extra_cmd @@ -55,6 +57,7 @@ setup( (base + "share/cmake/pybind11", cmake_files), (base + "share/pkgconfig", pkgconfig_files), (base + "include/pybind11", main_headers), + (base + "include/pybind11/conduit", conduit_headers), (base + "include/pybind11/detail", detail_headers), (base + "include/pybind11/eigen", eigen_headers), (base + "include/pybind11/stl", stl_headers), diff --git a/tools/setup_main.py.in b/tools/setup_main.py.in index 6358cc7b9..eb7b84ed6 100644 --- a/tools/setup_main.py.in +++ b/tools/setup_main.py.in @@ -14,6 +14,7 @@ setup( packages=[ "pybind11", "pybind11.include.pybind11", + "pybind11.include.pybind11.conduit", "pybind11.include.pybind11.detail", "pybind11.include.pybind11.eigen", "pybind11.include.pybind11.stl", @@ -23,6 +24,7 @@ setup( package_data={ "pybind11": ["py.typed"], "pybind11.include.pybind11": ["*.h"], + "pybind11.include.pybind11.conduit": ["*.h", "*.txt"], "pybind11.include.pybind11.detail": ["*.h"], "pybind11.include.pybind11.eigen": ["*.h"], "pybind11.include.pybind11.stl": ["*.h"],