Skip to content

Commit a90e2af

Browse files
authored
Factor out pybind11/conduit/pybind11_platform_abi_id.h (#5375)
* Factor out pybind11/compat/wrap_include_python_h.h * Fixes to resolve tests_packaging failures. * Factor out pybind11/compat/pybind11_platform_abi_id.h * Add pybind11/compat/README.txt and a couple source code comments. * Minor changes to comments. * Factor out pybind11/compat/pybind11_conduit_v1.h * Add long comment to pybind11/compat/pybind11_conduit_v1.h * Add pybind11/compat/README.txt to wheels. * Add `-fno-exceptions` to compiler options for exo_planet_c_api * 1. Move `target_compile_options()` into loop over test targets, in case the `"exo_planet_c_api"` target does not exist. 2. Add `-fno-exceptions` option also for `NVHPC`. 3. Also check for `__cpp_exceptions` in exo_planet_c_api.cpp. * 1. Fix accident (forgot to undo temporary change). 2. Special-case __EMSCRIPTEN__ in exo_planet_c_api.cpp * Give up on compiling exo_planet_c_api.cpp with MSVC `/EHs-c-`: There was one trouble maker (all other jobs worked): Visual Studio 15 2017: ``` cl : Command line warning D9025: overriding '/EHc' with '/EHc-' [C:\projects\pybind11\tests\exo_planet_c_api.vcxproj] ... C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\xlocale(319): error C2220: warning treated as error - no 'object' file generated [C:\projects\pybind11\tests\exo_planet_c_api.vcxproj] C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\xlocale(319): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc ``` * Move pybind11/compat to pybind11/conduit as suggested by @henryiii: #5375 (review)
1 parent ec9c268 commit a90e2af

12 files changed

+337
-182
lines changed

CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ set(PYBIND11_HEADERS
143143
include/pybind11/chrono.h
144144
include/pybind11/common.h
145145
include/pybind11/complex.h
146+
include/pybind11/conduit/pybind11_conduit_v1.h
147+
include/pybind11/conduit/pybind11_platform_abi_id.h
148+
include/pybind11/conduit/wrap_include_python_h.h
146149
include/pybind11/options.h
147150
include/pybind11/eigen.h
148151
include/pybind11/eigen/common.h

include/pybind11/conduit/README.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
NOTE
2+
----
3+
4+
The C++ code here
5+
6+
** only depends on <Python.h> **
7+
8+
and nothing else.
9+
10+
DO NOT ADD CODE WITH OTHER EXTERNAL DEPENDENCIES TO THIS DIRECTORY.
11+
12+
Read on:
13+
14+
pybind11_conduit_v1.h — Type-safe interoperability between different
15+
independent Python/C++ bindings systems.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright (c) 2024 The pybind Community.
2+
3+
/* The pybind11_conduit_v1 feature enables type-safe interoperability between
4+
5+
* different independent Python/C++ bindings systems,
6+
7+
* including pybind11 versions with different PYBIND11_INTERNALS_VERSION's.
8+
9+
The naming of the feature is a bit misleading:
10+
11+
* The feature is in no way tied to pybind11 internals.
12+
13+
* It just happens to originate from pybind11 and currently still lives there.
14+
15+
* The only external dependency is <Python.h>.
16+
17+
The implementation is a VERY light-weight dependency. It is designed to be
18+
compatible with any ISO C++11 (or higher) compiler, and does NOT require
19+
C++ Exception Handling to be enabled.
20+
21+
Please see https://github.com/pybind/pybind11/pull/5296 for more background.
22+
23+
The implementation involves a
24+
25+
def _pybind11_conduit_v1_(
26+
self,
27+
pybind11_platform_abi_id: bytes,
28+
cpp_type_info_capsule: capsule,
29+
pointer_kind: bytes) -> capsule
30+
31+
method that is meant to be added to Python objects wrapping C++ objects
32+
(e.g. pybind11::class_-wrapped types).
33+
34+
The design of the _pybind11_conduit_v1_ feature provides two layers of
35+
protection against C++ ABI mismatches:
36+
37+
* The first and most important layer is that the pybind11_platform_abi_id's
38+
must match between extensions. — This will never be perfect, but is the same
39+
pragmatic approach used in pybind11 since 2017
40+
(https://github.com/pybind/pybind11/commit/96997a4b9d4ec3d389a570604394af5d5eee2557,
41+
PYBIND11_INTERNALS_ID).
42+
43+
* The second layer is that the typeid(std::type_info).name()'s must match
44+
between extensions.
45+
46+
The implementation below (which is shorter than this comment!), serves as a
47+
battle-tested specification. The main API is this one function:
48+
49+
auto *cpp_pointer = pybind11_conduit_v1::get_type_pointer_ephemeral<YourType>(py_obj);
50+
51+
It is meant to be a minimalistic reference implementation, intentionally
52+
without comprehensive error reporting. It is expected that major bindings
53+
systems will roll their own, compatible implementations, potentially with
54+
system-specific error reporting. The essential specifications all bindings
55+
systems need to agree on are merely:
56+
57+
* PYBIND11_PLATFORM_ABI_ID (const char* literal).
58+
59+
* The cpp_type_info capsule (see below: a void *ptr and a const char *name).
60+
61+
* The cpp_conduit capsule (see below: a void *ptr and a const char *name).
62+
63+
* "raw_pointer_ephemeral" means: the lifetime of the pointer is the lifetime
64+
of the py_obj.
65+
66+
*/
67+
68+
// THIS MUST STAY AT THE TOP!
69+
#include "pybind11_platform_abi_id.h"
70+
71+
#include <Python.h>
72+
#include <typeinfo>
73+
74+
namespace pybind11_conduit_v1 {
75+
76+
inline void *get_raw_pointer_ephemeral(PyObject *py_obj, const std::type_info *cpp_type_info) {
77+
PyObject *cpp_type_info_capsule
78+
= PyCapsule_New(const_cast<void *>(static_cast<const void *>(cpp_type_info)),
79+
typeid(std::type_info).name(),
80+
nullptr);
81+
if (cpp_type_info_capsule == nullptr) {
82+
return nullptr;
83+
}
84+
PyObject *cpp_conduit = PyObject_CallMethod(py_obj,
85+
"_pybind11_conduit_v1_",
86+
"yOy",
87+
PYBIND11_PLATFORM_ABI_ID,
88+
cpp_type_info_capsule,
89+
"raw_pointer_ephemeral");
90+
Py_DECREF(cpp_type_info_capsule);
91+
if (cpp_conduit == nullptr) {
92+
return nullptr;
93+
}
94+
void *raw_ptr = PyCapsule_GetPointer(cpp_conduit, cpp_type_info->name());
95+
Py_DECREF(cpp_conduit);
96+
if (PyErr_Occurred()) {
97+
return nullptr;
98+
}
99+
return raw_ptr;
100+
}
101+
102+
template <typename T>
103+
T *get_type_pointer_ephemeral(PyObject *py_obj) {
104+
void *raw_ptr = get_raw_pointer_ephemeral(py_obj, &typeid(T));
105+
if (raw_ptr == nullptr) {
106+
return nullptr;
107+
}
108+
return static_cast<T *>(raw_ptr);
109+
}
110+
111+
} // namespace pybind11_conduit_v1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#pragma once
2+
3+
// Copyright (c) 2024 The pybind Community.
4+
5+
// To maximize reusability:
6+
// DO NOT ADD CODE THAT REQUIRES C++ EXCEPTION HANDLING.
7+
8+
#include "wrap_include_python_h.h"
9+
10+
// Implementation details. DO NOT USE ELSEWHERE. (Unfortunately we cannot #undef them.)
11+
// This is duplicated here to maximize portability.
12+
#define PYBIND11_PLATFORM_ABI_ID_STRINGIFY(x) #x
13+
#define PYBIND11_PLATFORM_ABI_ID_TOSTRING(x) PYBIND11_PLATFORM_ABI_ID_STRINGIFY(x)
14+
15+
// On MSVC, debug and release builds are not ABI-compatible!
16+
#if defined(_MSC_VER) && defined(_DEBUG)
17+
# define PYBIND11_BUILD_TYPE "_debug"
18+
#else
19+
# define PYBIND11_BUILD_TYPE ""
20+
#endif
21+
22+
// Let's assume that different compilers are ABI-incompatible.
23+
// A user can manually set this string if they know their
24+
// compiler is compatible.
25+
#ifndef PYBIND11_COMPILER_TYPE
26+
# if defined(_MSC_VER)
27+
# define PYBIND11_COMPILER_TYPE "_msvc"
28+
# elif defined(__INTEL_COMPILER)
29+
# define PYBIND11_COMPILER_TYPE "_icc"
30+
# elif defined(__clang__)
31+
# define PYBIND11_COMPILER_TYPE "_clang"
32+
# elif defined(__PGI)
33+
# define PYBIND11_COMPILER_TYPE "_pgi"
34+
# elif defined(__MINGW32__)
35+
# define PYBIND11_COMPILER_TYPE "_mingw"
36+
# elif defined(__CYGWIN__)
37+
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
38+
# elif defined(__GNUC__)
39+
# define PYBIND11_COMPILER_TYPE "_gcc"
40+
# else
41+
# define PYBIND11_COMPILER_TYPE "_unknown"
42+
# endif
43+
#endif
44+
45+
// Also standard libs
46+
#ifndef PYBIND11_STDLIB
47+
# if defined(_LIBCPP_VERSION)
48+
# define PYBIND11_STDLIB "_libcpp"
49+
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
50+
# define PYBIND11_STDLIB "_libstdcpp"
51+
# else
52+
# define PYBIND11_STDLIB ""
53+
# endif
54+
#endif
55+
56+
#ifndef PYBIND11_BUILD_ABI
57+
# if defined(__GXX_ABI_VERSION) // Linux/OSX.
58+
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_PLATFORM_ABI_ID_TOSTRING(__GXX_ABI_VERSION)
59+
# elif defined(_MSC_VER) // See PR #4953.
60+
# if defined(_MT) && defined(_DLL) // Corresponding to CL command line options /MD or /MDd.
61+
# if (_MSC_VER) / 100 == 19
62+
# define PYBIND11_BUILD_ABI "_md_mscver19"
63+
# else
64+
# error "Unknown major version for MSC_VER: PLEASE REVISE THIS CODE."
65+
# endif
66+
# elif defined(_MT) // Corresponding to CL command line options /MT or /MTd.
67+
# define PYBIND11_BUILD_ABI "_mt_mscver" PYBIND11_PLATFORM_ABI_ID_TOSTRING(_MSC_VER)
68+
# else
69+
# if (_MSC_VER) / 100 == 19
70+
# define PYBIND11_BUILD_ABI "_none_mscver19"
71+
# else
72+
# error "Unknown major version for MSC_VER: PLEASE REVISE THIS CODE."
73+
# endif
74+
# endif
75+
# elif defined(__NVCOMPILER) // NVHPC (PGI-based).
76+
# define PYBIND11_BUILD_ABI "" // TODO: What should be here, to prevent UB?
77+
# else
78+
# error "Unknown platform or compiler: PLEASE REVISE THIS CODE."
79+
# endif
80+
#endif
81+
82+
#ifndef PYBIND11_INTERNALS_KIND
83+
# define PYBIND11_INTERNALS_KIND ""
84+
#endif
85+
86+
#define PYBIND11_PLATFORM_ABI_ID \
87+
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
88+
PYBIND11_BUILD_TYPE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#pragma once
2+
3+
// Copyright (c) 2024 The pybind Community.
4+
5+
// STRONG REQUIREMENT:
6+
// This header is a wrapper around `#include <Python.h>`, therefore it
7+
// MUST BE INCLUDED BEFORE ANY STANDARD HEADERS are included.
8+
// See also:
9+
// https://docs.python.org/3/c-api/intro.html#include-files
10+
// Quoting from there:
11+
// Note: Since Python may define some pre-processor definitions which affect
12+
// the standard headers on some systems, you must include Python.h before
13+
// any standard headers are included.
14+
15+
// To maximize reusability:
16+
// DO NOT ADD CODE THAT REQUIRES C++ EXCEPTION HANDLING.
17+
18+
// Disable linking to pythonX_d.lib on Windows in debug mode.
19+
#if defined(_MSC_VER) && defined(_DEBUG) && !defined(Py_DEBUG)
20+
// Workaround for a VS 2022 issue.
21+
// See https://github.com/pybind/pybind11/pull/3497 for full context.
22+
// NOTE: This workaround knowingly violates the Python.h include order
23+
// requirement (see above).
24+
# include <yvals.h>
25+
# if _MSVC_STL_VERSION >= 143
26+
# include <crtdefs.h>
27+
# endif
28+
# define PYBIND11_DEBUG_MARKER
29+
# undef _DEBUG
30+
#endif
31+
32+
// Don't let Python.h #define (v)snprintf as macro because they are implemented
33+
// properly in Visual Studio since 2015.
34+
#if defined(_MSC_VER)
35+
# define HAVE_SNPRINTF 1
36+
#endif
37+
38+
#if defined(_MSC_VER)
39+
# pragma warning(push)
40+
# pragma warning(disable : 4505)
41+
// C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed
42+
#endif
43+
44+
#include <Python.h>
45+
#include <frameobject.h>
46+
#include <pythread.h>
47+
48+
#if defined(_MSC_VER)
49+
# pragma warning(pop)
50+
#endif
51+
52+
#if defined(PYBIND11_DEBUG_MARKER)
53+
# define _DEBUG
54+
# undef PYBIND11_DEBUG_MARKER
55+
#endif
56+
57+
// Python #defines overrides on all sorts of core functions, which
58+
// tends to wreak havok in C++ codebases that expect these to work
59+
// like regular functions (potentially with several overloads).
60+
#if defined(isalnum)
61+
# undef isalnum
62+
# undef isalpha
63+
# undef islower
64+
# undef isspace
65+
# undef isupper
66+
# undef tolower
67+
# undef toupper
68+
#endif
69+
70+
#if defined(copysign)
71+
# undef copysign
72+
#endif

include/pybind11/detail/common.h

+5-57
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99

1010
#pragma once
1111

12+
#include <pybind11/conduit/wrap_include_python_h.h>
13+
#if PY_VERSION_HEX < 0x03080000
14+
# error "PYTHON < 3.8 IS UNSUPPORTED. pybind11 v2.13 was the last to support Python 3.7."
15+
#endif
16+
1217
#define PYBIND11_VERSION_MAJOR 2
1318
#define PYBIND11_VERSION_MINOR 14
1419
#define PYBIND11_VERSION_PATCH 0.dev1
@@ -204,31 +209,6 @@
204209
# define PYBIND11_MAYBE_UNUSED __attribute__((__unused__))
205210
#endif
206211

207-
/* Don't let Python.h #define (v)snprintf as macro because they are implemented
208-
properly in Visual Studio since 2015. */
209-
#if defined(_MSC_VER)
210-
# define HAVE_SNPRINTF 1
211-
#endif
212-
213-
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
214-
#if defined(_MSC_VER)
215-
PYBIND11_WARNING_PUSH
216-
PYBIND11_WARNING_DISABLE_MSVC(4505)
217-
// C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only)
218-
# if defined(_DEBUG) && !defined(Py_DEBUG)
219-
// Workaround for a VS 2022 issue.
220-
// NOTE: This workaround knowingly violates the Python.h include order requirement:
221-
// https://docs.python.org/3/c-api/intro.html#include-files
222-
// See https://github.com/pybind/pybind11/pull/3497 for full context.
223-
# include <yvals.h>
224-
# if _MSVC_STL_VERSION >= 143
225-
# include <crtdefs.h>
226-
# endif
227-
# define PYBIND11_DEBUG_MARKER
228-
# undef _DEBUG
229-
# endif
230-
#endif
231-
232212
// https://en.cppreference.com/w/c/chrono/localtime
233213
#if defined(__STDC_LIB_EXT1__) && !defined(__STDC_WANT_LIB_EXT1__)
234214
# define __STDC_WANT_LIB_EXT1__
@@ -263,30 +243,6 @@ PYBIND11_WARNING_DISABLE_MSVC(4505)
263243
# endif
264244
#endif
265245

266-
#include <Python.h>
267-
#if PY_VERSION_HEX < 0x03080000
268-
# error "PYTHON < 3.8 IS UNSUPPORTED. pybind11 v2.13 was the last to support Python 3.7."
269-
#endif
270-
#include <frameobject.h>
271-
#include <pythread.h>
272-
273-
/* Python #defines overrides on all sorts of core functions, which
274-
tends to weak havok in C++ codebases that expect these to work
275-
like regular functions (potentially with several overloads) */
276-
#if defined(isalnum)
277-
# undef isalnum
278-
# undef isalpha
279-
# undef islower
280-
# undef isspace
281-
# undef isupper
282-
# undef tolower
283-
# undef toupper
284-
#endif
285-
286-
#if defined(copysign)
287-
# undef copysign
288-
#endif
289-
290246
#if defined(PYBIND11_NUMPY_1_ONLY)
291247
# define PYBIND11_INTERNAL_NUMPY_1_ONLY_DETECTED
292248
#endif
@@ -295,14 +251,6 @@ PYBIND11_WARNING_DISABLE_MSVC(4505)
295251
# define PYBIND11_SIMPLE_GIL_MANAGEMENT
296252
#endif
297253

298-
#if defined(_MSC_VER)
299-
# if defined(PYBIND11_DEBUG_MARKER)
300-
# define _DEBUG
301-
# undef PYBIND11_DEBUG_MARKER
302-
# endif
303-
PYBIND11_WARNING_POP
304-
#endif
305-
306254
#include <cstddef>
307255
#include <cstring>
308256
#include <exception>

0 commit comments

Comments
 (0)