Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-09 10:20:46

0001 // Copyright (c) 2024 The pybind Community.
0002 
0003 /* The pybind11_conduit_v1 feature enables type-safe interoperability between
0004 
0005 * different independent Python/C++ bindings systems,
0006 
0007 * including pybind11 versions with different PYBIND11_INTERNALS_VERSION's.
0008 
0009     * NOTE: The conduit feature
0010             only covers    from-Python-to-C++ conversions, it
0011             does not cover from-C++-to-Python conversions.
0012             (For the latter, a different feature would have to be added.)
0013 
0014 The naming of the feature is a bit misleading:
0015 
0016 * The feature is in no way tied to pybind11 internals.
0017 
0018 * It just happens to originate from pybind11 and currently still lives there.
0019 
0020 * The only external dependency is <Python.h>.
0021 
0022 The implementation is a VERY light-weight dependency. It is designed to be
0023 compatible with any ISO C++11 (or higher) compiler, and does NOT require
0024 C++ Exception Handling to be enabled.
0025 
0026 Please see https://github.com/pybind/pybind11/pull/5296 for more background.
0027 
0028 The implementation involves a
0029 
0030 def _pybind11_conduit_v1_(
0031     self,
0032     pybind11_platform_abi_id: bytes,
0033     cpp_type_info_capsule: capsule,
0034     pointer_kind: bytes) -> capsule
0035 
0036 method that is meant to be added to Python objects wrapping C++ objects
0037 (e.g. pybind11::class_-wrapped types).
0038 
0039 The design of the _pybind11_conduit_v1_ feature provides two layers of
0040 protection against C++ ABI mismatches:
0041 
0042 * The first and most important layer is that the pybind11_platform_abi_id's
0043   must match between extensions. — This will never be perfect, but is the same
0044   pragmatic approach used in pybind11 since 2017
0045   (https://github.com/pybind/pybind11/commit/96997a4b9d4ec3d389a570604394af5d5eee2557,
0046   PYBIND11_INTERNALS_ID).
0047 
0048 * The second layer is that the typeid(std::type_info).name()'s must match
0049   between extensions.
0050 
0051 The implementation below (which is shorter than this comment!), serves as a
0052 battle-tested specification. The main API is this one function:
0053 
0054 auto *cpp_pointer = pybind11_conduit_v1::get_type_pointer_ephemeral<YourType>(py_obj);
0055 
0056 It is meant to be a minimalistic reference implementation, intentionally
0057 without comprehensive error reporting. It is expected that major bindings
0058 systems will roll their own, compatible implementations, potentially with
0059 system-specific error reporting. The essential specifications all bindings
0060 systems need to agree on are merely:
0061 
0062 * PYBIND11_PLATFORM_ABI_ID (const char* literal).
0063 
0064 * The cpp_type_info capsule (see below: a void *ptr and a const char *name).
0065 
0066 * The cpp_conduit capsule (see below: a void *ptr and a const char *name).
0067 
0068 * "raw_pointer_ephemeral" means: the lifetime of the pointer is the lifetime
0069   of the py_obj.
0070 
0071 */
0072 
0073 // THIS MUST STAY AT THE TOP!
0074 #include "pybind11_platform_abi_id.h"
0075 
0076 #include <Python.h>
0077 #include <typeinfo>
0078 
0079 namespace pybind11_conduit_v1 {
0080 
0081 inline void *get_raw_pointer_ephemeral(PyObject *py_obj, const std::type_info *cpp_type_info) {
0082     PyObject *cpp_type_info_capsule
0083         = PyCapsule_New(const_cast<void *>(static_cast<const void *>(cpp_type_info)),
0084                         typeid(std::type_info).name(),
0085                         nullptr);
0086     if (cpp_type_info_capsule == nullptr) {
0087         return nullptr;
0088     }
0089     PyObject *cpp_conduit = PyObject_CallMethod(py_obj,
0090                                                 "_pybind11_conduit_v1_",
0091                                                 "yOy",
0092                                                 PYBIND11_PLATFORM_ABI_ID,
0093                                                 cpp_type_info_capsule,
0094                                                 "raw_pointer_ephemeral");
0095     Py_DECREF(cpp_type_info_capsule);
0096     if (cpp_conduit == nullptr) {
0097         return nullptr;
0098     }
0099     void *raw_ptr = PyCapsule_GetPointer(cpp_conduit, cpp_type_info->name());
0100     Py_DECREF(cpp_conduit);
0101     if (PyErr_Occurred()) {
0102         return nullptr;
0103     }
0104     return raw_ptr;
0105 }
0106 
0107 template <typename T>
0108 T *get_type_pointer_ephemeral(PyObject *py_obj) {
0109     void *raw_ptr = get_raw_pointer_ephemeral(py_obj, &typeid(T));
0110     if (raw_ptr == nullptr) {
0111         return nullptr;
0112     }
0113     return static_cast<T *>(raw_ptr);
0114 }
0115 
0116 } // namespace pybind11_conduit_v1