ext: Update pybind11 to v2.8.1
Change-Id: Ia1c7081377f53fd470addf35526f8b28a949a7b0 Signed-off-by: Jason Lowe-Power <jason@lowepower.com> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/52523 Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Gabe Black <gabe.black@gmail.com>
This commit is contained in:
committed by
Jason Lowe-Power
parent
ba5f68db3d
commit
1e8aeee698
@@ -12,13 +12,17 @@
|
||||
|
||||
#include "cast.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
/// \addtogroup annotations
|
||||
/// @{
|
||||
|
||||
/// Annotation for methods
|
||||
struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };
|
||||
struct is_method { handle class_;
|
||||
explicit is_method(const handle &c) : class_(c) {}
|
||||
};
|
||||
|
||||
/// Annotation for operators
|
||||
struct is_operator { };
|
||||
@@ -27,16 +31,24 @@ struct is_operator { };
|
||||
struct is_final { };
|
||||
|
||||
/// Annotation for parent scope
|
||||
struct scope { handle value; scope(const handle &s) : value(s) { } };
|
||||
struct scope { handle value;
|
||||
explicit scope(const handle &s) : value(s) {}
|
||||
};
|
||||
|
||||
/// Annotation for documentation
|
||||
struct doc { const char *value; doc(const char *value) : value(value) { } };
|
||||
struct doc { const char *value;
|
||||
explicit doc(const char *value) : value(value) {}
|
||||
};
|
||||
|
||||
/// Annotation for function names
|
||||
struct name { const char *value; name(const char *value) : value(value) { } };
|
||||
struct name { const char *value;
|
||||
explicit 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()) { } };
|
||||
struct sibling { handle value;
|
||||
explicit sibling(const handle &value) : value(value.ptr()) {}
|
||||
};
|
||||
|
||||
/// Annotation indicating that a class derives from another given type
|
||||
template <typename T> struct base {
|
||||
@@ -62,14 +74,34 @@ struct metaclass {
|
||||
handle value;
|
||||
|
||||
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
|
||||
metaclass() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
|
||||
metaclass() {}
|
||||
|
||||
/// Override pybind11's default metaclass
|
||||
explicit metaclass(handle value) : value(value) { }
|
||||
};
|
||||
|
||||
/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that
|
||||
/// may be used to customize the Python type.
|
||||
///
|
||||
/// The callback is invoked immediately before `PyType_Ready`.
|
||||
///
|
||||
/// Note: This is an advanced interface, and uses of it may require changes to
|
||||
/// work with later versions of pybind11. You may wish to consult the
|
||||
/// implementation of `make_new_python_type` in `detail/classes.h` to understand
|
||||
/// the context in which the callback will be run.
|
||||
struct custom_type_setup {
|
||||
using callback = std::function<void(PyHeapTypeObject *heap_type)>;
|
||||
|
||||
explicit custom_type_setup(callback value) : value(std::move(value)) {}
|
||||
|
||||
callback value;
|
||||
};
|
||||
|
||||
/// Annotation that marks a class as local to the module:
|
||||
struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } };
|
||||
struct module_local { const bool value;
|
||||
constexpr explicit module_local(bool v = true) : value(v) {}
|
||||
};
|
||||
|
||||
/// Annotation to mark enums as an arithmetic type
|
||||
struct arithmetic { };
|
||||
@@ -123,7 +155,7 @@ 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_;
|
||||
inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
|
||||
void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
|
||||
|
||||
/// Internal data structure which holds metadata about a keyword argument
|
||||
struct argument_record {
|
||||
@@ -259,6 +291,9 @@ struct type_record {
|
||||
/// Custom metaclass (optional)
|
||||
handle metaclass;
|
||||
|
||||
/// Custom type setup.
|
||||
custom_type_setup::callback custom_type_setup_callback;
|
||||
|
||||
/// Multiple inheritance marker
|
||||
bool multiple_inheritance : 1;
|
||||
|
||||
@@ -377,7 +412,7 @@ template <> struct process_attribute<is_new_style_constructor> : process_attribu
|
||||
};
|
||||
|
||||
inline void process_kw_only_arg(const arg &a, function_record *r) {
|
||||
if (!a.name || strlen(a.name) == 0)
|
||||
if (!a.name || a.name[0] == '\0')
|
||||
pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation");
|
||||
++r->nargs_kw_only;
|
||||
}
|
||||
@@ -463,6 +498,13 @@ 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<custom_type_setup> {
|
||||
static void init(const custom_type_setup &value, type_record *r) {
|
||||
r->custom_type_setup_callback = value.value;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct process_attribute<is_final> : process_attribute_default<is_final> {
|
||||
static void init(const is_final &, type_record *r) { r->is_final = true; }
|
||||
@@ -515,20 +557,31 @@ template <size_t Nurse, size_t Patient> struct process_attribute<keep_alive<Nurs
|
||||
/// 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);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
||||
using expander = int[];
|
||||
(void) expander{
|
||||
0, ((void) process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
||||
}
|
||||
static void init(const Args&... args, type_record *r) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
|
||||
ignore_unused(unused);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
||||
using expander = int[];
|
||||
(void) expander{0,
|
||||
(process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
||||
}
|
||||
static void precall(function_call &call) {
|
||||
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::precall(call), 0) ... };
|
||||
ignore_unused(unused);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call);
|
||||
using expander = int[];
|
||||
(void) expander{0,
|
||||
(process_attribute<typename std::decay<Args>::type>::precall(call), 0)...};
|
||||
}
|
||||
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);
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret);
|
||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret);
|
||||
using expander = int[];
|
||||
(void) expander{
|
||||
0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0)...};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -544,6 +597,7 @@ 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) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs);
|
||||
return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ struct buffer_info {
|
||||
view->strides
|
||||
? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
|
||||
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
|
||||
view->readonly) {
|
||||
(view->readonly != 0)) {
|
||||
this->m_view = view;
|
||||
this->ownview = ownview;
|
||||
}
|
||||
@@ -91,11 +91,9 @@ struct buffer_info {
|
||||
buffer_info(const buffer_info &) = delete;
|
||||
buffer_info& operator=(const buffer_info &) = delete;
|
||||
|
||||
buffer_info(buffer_info &&other) {
|
||||
(*this) = std::move(other);
|
||||
}
|
||||
buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }
|
||||
|
||||
buffer_info& operator=(buffer_info &&rhs) {
|
||||
buffer_info &operator=(buffer_info &&rhs) noexcept {
|
||||
ptr = rhs.ptr;
|
||||
itemsize = rhs.itemsize;
|
||||
size = rhs.size;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <datetime.h>
|
||||
|
||||
// Backport the PyDateTime_DELTA functions from Python3.3 if required
|
||||
@@ -35,7 +40,7 @@ public:
|
||||
using rep = typename type::rep;
|
||||
using period = typename type::period;
|
||||
|
||||
using days = std::chrono::duration<uint_fast32_t, std::ratio<86400>>;
|
||||
using days = std::chrono::duration<int_least32_t, std::ratio<86400>>; // signed 25 bits required by the standard.
|
||||
|
||||
bool load(handle src, bool) {
|
||||
using namespace std::chrono;
|
||||
@@ -53,11 +58,11 @@ public:
|
||||
return true;
|
||||
}
|
||||
// If invoked with a float we assume it is seconds and convert
|
||||
else if (PyFloat_Check(src.ptr())) {
|
||||
if (PyFloat_Check(src.ptr())) {
|
||||
value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr()))));
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is a duration just return it back
|
||||
@@ -95,6 +100,22 @@ public:
|
||||
PYBIND11_TYPE_CASTER(type, _("datetime.timedelta"));
|
||||
};
|
||||
|
||||
inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
|
||||
#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || defined(_MSC_VER)
|
||||
if (localtime_s(buf, time))
|
||||
return nullptr;
|
||||
return buf;
|
||||
#else
|
||||
static std::mutex mtx;
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
std::tm *tm_ptr = localtime(time);
|
||||
if (tm_ptr != nullptr) {
|
||||
*buf = *tm_ptr;
|
||||
}
|
||||
return tm_ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This is for casting times on the system clock into datetime.datetime instances
|
||||
template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
||||
public:
|
||||
@@ -161,10 +182,11 @@ public:
|
||||
// > If std::time_t has lower precision, it is implementation-defined whether the value is rounded or truncated.
|
||||
// (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
|
||||
std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
|
||||
// this function uses static memory so it's best to copy it out asap just in case
|
||||
// otherwise other code that is using localtime may break this (not just python code)
|
||||
std::tm localtime = *std::localtime(&tt);
|
||||
|
||||
std::tm localtime;
|
||||
std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
|
||||
if (!localtime_ptr)
|
||||
throw cast_error("Unable to represent system_clock in local time");
|
||||
return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
|
||||
localtime.tm_mon + 1,
|
||||
localtime.tm_mday,
|
||||
|
||||
@@ -129,8 +129,9 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb
|
||||
// 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 && value && PyObject_IsInstance(descr, static_prop)
|
||||
&& !PyObject_IsInstance(value, static_prop);
|
||||
const auto call_descr_set = (descr != nullptr) && (value != nullptr)
|
||||
&& (PyObject_IsInstance(descr, static_prop) != 0)
|
||||
&& (PyObject_IsInstance(value, static_prop) == 0);
|
||||
if (call_descr_set) {
|
||||
// Call `static_property.__set__()` instead of replacing the `static_property`.
|
||||
#if !defined(PYPY_VERSION)
|
||||
@@ -162,9 +163,7 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name
|
||||
Py_INCREF(descr);
|
||||
return descr;
|
||||
}
|
||||
else {
|
||||
return PyType_Type.tp_getattro(obj, name);
|
||||
}
|
||||
return PyType_Type.tp_getattro(obj, name);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -211,7 +210,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
||||
internals.direct_conversions.erase(tindex);
|
||||
|
||||
if (tinfo->module_local)
|
||||
registered_local_types_cpp().erase(tindex);
|
||||
get_local_internals().registered_types_cpp.erase(tindex);
|
||||
else
|
||||
internals.registered_types_cpp.erase(tindex);
|
||||
internals.registered_types_py.erase(tinfo->type);
|
||||
@@ -329,7 +328,7 @@ inline bool deregister_instance(instance *self, void *valptr, const type_info *t
|
||||
inline PyObject *make_new_instance(PyTypeObject *type) {
|
||||
#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.
|
||||
// object is 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;
|
||||
@@ -564,7 +563,7 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
|
||||
view->len = view->itemsize;
|
||||
for (auto s : info->shape)
|
||||
view->len *= s;
|
||||
view->readonly = info->readonly;
|
||||
view->readonly = static_cast<int>(info->readonly);
|
||||
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
|
||||
view->format = const_cast<char *>(info->format.c_str());
|
||||
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
|
||||
@@ -684,11 +683,13 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
if (rec.buffer_protocol)
|
||||
enable_buffer_protocol(heap_type);
|
||||
|
||||
if (rec.custom_type_setup_callback)
|
||||
rec.custom_type_setup_callback(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));
|
||||
assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
|
||||
/* Register type with the parent scope */
|
||||
if (rec.scope)
|
||||
|
||||
@@ -10,8 +10,12 @@
|
||||
#pragma once
|
||||
|
||||
#define PYBIND11_VERSION_MAJOR 2
|
||||
#define PYBIND11_VERSION_MINOR 6
|
||||
#define PYBIND11_VERSION_PATCH 2
|
||||
#define PYBIND11_VERSION_MINOR 8
|
||||
#define PYBIND11_VERSION_PATCH 1
|
||||
|
||||
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
|
||||
// Additional convention: 0xD = dev
|
||||
#define PYBIND11_VERSION_HEX 0x02080100
|
||||
|
||||
#define PYBIND11_NAMESPACE_BEGIN(name) namespace name {
|
||||
#define PYBIND11_NAMESPACE_END(name) }
|
||||
@@ -52,6 +56,9 @@
|
||||
# elif __INTEL_COMPILER < 1900 && defined(PYBIND11_CPP14)
|
||||
# error pybind11 supports only C++11 with Intel C++ compiler v18. Use v19 or newer for C++14.
|
||||
# endif
|
||||
/* The following pragma cannot be pop'ed:
|
||||
https://community.intel.com/t5/Intel-C-Compiler/Inline-and-no-inline-warning/td-p/1216764 */
|
||||
# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline"
|
||||
#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
|
||||
@@ -82,13 +89,43 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define PYBIND11_NOINLINE __declspec(noinline)
|
||||
#else
|
||||
# define PYBIND11_NOINLINE __attribute__ ((noinline))
|
||||
#if !defined(PYBIND11_EXPORT_EXCEPTION)
|
||||
# ifdef __MINGW32__
|
||||
// workaround for:
|
||||
// error: 'dllexport' implies default visibility, but xxx has already been declared with a different visibility
|
||||
# define PYBIND11_EXPORT_EXCEPTION
|
||||
# else
|
||||
# define PYBIND11_EXPORT_EXCEPTION PYBIND11_EXPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(PYBIND11_CPP14)
|
||||
// For CUDA, GCC7, GCC8:
|
||||
// PYBIND11_NOINLINE_FORCED is incompatible with `-Wattributes -Werror`.
|
||||
// When defining PYBIND11_NOINLINE_FORCED, it is best to also use `-Wno-attributes`.
|
||||
// However, the measured shared-library size saving when using noinline are only
|
||||
// 1.7% for CUDA, -0.2% for GCC7, and 0.0% for GCC8 (using -DCMAKE_BUILD_TYPE=MinSizeRel,
|
||||
// the default under pybind11/tests).
|
||||
#if !defined(PYBIND11_NOINLINE_FORCED) && \
|
||||
(defined(__CUDACC__) || (defined(__GNUC__) && (__GNUC__ == 7 || __GNUC__ == 8)))
|
||||
# define PYBIND11_NOINLINE_DISABLED
|
||||
#endif
|
||||
|
||||
// The PYBIND11_NOINLINE macro is for function DEFINITIONS.
|
||||
// In contrast, FORWARD DECLARATIONS should never use this macro:
|
||||
// https://stackoverflow.com/questions/9317473/forward-declaration-of-inline-functions
|
||||
#if defined(PYBIND11_NOINLINE_DISABLED) // Option for maximum portability and experimentation.
|
||||
# define PYBIND11_NOINLINE inline
|
||||
#elif defined(_MSC_VER)
|
||||
# define PYBIND11_NOINLINE __declspec(noinline) inline
|
||||
#else
|
||||
# define PYBIND11_NOINLINE __attribute__ ((noinline)) inline
|
||||
#endif
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
// For unknown reasons all PYBIND11_DEPRECATED member trigger a warning when declared
|
||||
// whether it is used or not
|
||||
# define PYBIND11_DEPRECATED(reason)
|
||||
#elif defined(PYBIND11_CPP14)
|
||||
# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]]
|
||||
#else
|
||||
# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason)))
|
||||
@@ -114,13 +151,38 @@
|
||||
# define HAVE_ROUND 1
|
||||
# endif
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4510 4610 4512 4005)
|
||||
// C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only)
|
||||
# pragma warning(disable: 4505)
|
||||
# if defined(_DEBUG) && !defined(Py_DEBUG)
|
||||
# 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__
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
// std::optional (but including it in c++14 mode isn't allowed)
|
||||
# if defined(PYBIND11_CPP17) && __has_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>) && \
|
||||
!__has_include(<optional>))
|
||||
# define PYBIND11_HAS_EXP_OPTIONAL 1
|
||||
# endif
|
||||
// std::variant
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<variant>)
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
# endif
|
||||
#elif defined(_MSC_VER) && defined(PYBIND11_CPP17)
|
||||
# define PYBIND11_HAS_OPTIONAL 1
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
#include <frameobject.h>
|
||||
#include <pythread.h>
|
||||
@@ -162,6 +224,24 @@
|
||||
#include <memory>
|
||||
#include <typeindex>
|
||||
#include <type_traits>
|
||||
#if defined(__has_include)
|
||||
# if __has_include(<version>)
|
||||
# include <version>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// #define PYBIND11_STR_LEGACY_PERMISSIVE
|
||||
// If DEFINED, pybind11::str can hold PyUnicodeObject or PyBytesObject
|
||||
// (probably surprising and never documented, but this was the
|
||||
// legacy behavior until and including v2.6.x). As a side-effect,
|
||||
// pybind11::isinstance<str>() is true for both pybind11::str and
|
||||
// pybind11::bytes.
|
||||
// If UNDEFINED, pybind11::str can only hold PyUnicodeObject, and
|
||||
// pybind11::isinstance<str>() is true only for pybind11::str.
|
||||
// However, for Python 2 only (!), the pybind11::str caster
|
||||
// implicitly decodes bytes to PyUnicodeObject. This is to ease
|
||||
// the transition from the legacy behavior to the non-permissive
|
||||
// behavior.
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
|
||||
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
|
||||
@@ -175,8 +255,8 @@
|
||||
#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_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o)
|
||||
#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o)
|
||||
#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o))
|
||||
#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o))
|
||||
#define PYBIND11_BYTES_NAME "bytes"
|
||||
#define PYBIND11_STRING_NAME "str"
|
||||
#define PYBIND11_SLICE_OBJECT PyObject
|
||||
@@ -254,6 +334,19 @@ extern "C" {
|
||||
} \
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
|
||||
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set &e) { \
|
||||
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
|
||||
return nullptr; \
|
||||
} catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
|
||||
#else
|
||||
|
||||
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
@@ -263,6 +356,8 @@ extern "C" {
|
||||
return nullptr; \
|
||||
} \
|
||||
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
***Deprecated in favor of PYBIND11_MODULE***
|
||||
|
||||
@@ -311,30 +406,35 @@ extern "C" {
|
||||
});
|
||||
}
|
||||
\endrst */
|
||||
#define PYBIND11_MODULE(name, variable) \
|
||||
static ::pybind11::module_::module_def \
|
||||
PYBIND11_CONCAT(pybind11_module_def_, name) PYBIND11_MAYBE_UNUSED; \
|
||||
PYBIND11_MAYBE_UNUSED \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
PYBIND11_PLUGIN_IMPL(name) { \
|
||||
PYBIND11_CHECK_PYTHON_VERSION \
|
||||
PYBIND11_ENSURE_INTERNALS_READY \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
PYBIND11_TOSTRING(name), nullptr, \
|
||||
&PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
|
||||
|
||||
#define PYBIND11_MODULE(name, variable) \
|
||||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
|
||||
PYBIND11_MAYBE_UNUSED; \
|
||||
PYBIND11_MAYBE_UNUSED \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
PYBIND11_PLUGIN_IMPL(name) { \
|
||||
PYBIND11_CHECK_PYTHON_VERSION \
|
||||
PYBIND11_ENSURE_INTERNALS_READY \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} \
|
||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
using ssize_t = Py_ssize_t;
|
||||
using size_t = std::size_t;
|
||||
|
||||
template <typename IntType>
|
||||
inline ssize_t ssize_t_cast(const IntType &val) {
|
||||
static_assert(sizeof(IntType) <= sizeof(ssize_t), "Implicit narrowing is not permitted.");
|
||||
return static_cast<ssize_t>(val);
|
||||
}
|
||||
|
||||
/// 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
|
||||
@@ -690,9 +790,6 @@ using function_signature_t = conditional_t<
|
||||
template <typename T> using is_lambda = satisfies_none_of<remove_reference_t<T>,
|
||||
std::is_function, std::is_pointer, std::is_member_pointer>;
|
||||
|
||||
/// Ignore that a variable is unused in compiler warnings
|
||||
inline void ignore_unused(const int *) { }
|
||||
|
||||
// [workaround(intel)] Internal error on fold expression
|
||||
/// Apply a function over each element of a parameter pack
|
||||
#if defined(__cpp_fold_expressions) && !defined(__INTEL_COMPILER)
|
||||
@@ -705,16 +802,23 @@ using expand_side_effects = bool[];
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4275) // warning C4275: An exported class was derived from a class that wasn't exported. Can be ignored when derived from a STL class.
|
||||
#endif
|
||||
/// C++ bindings of builtin Python exceptions
|
||||
class builtin_exception : public std::runtime_error {
|
||||
class PYBIND11_EXPORT_EXCEPTION 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;
|
||||
};
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#define PYBIND11_RUNTIME_EXCEPTION(name, type) \
|
||||
class name : public builtin_exception { public: \
|
||||
class PYBIND11_EXPORT_EXCEPTION name : public builtin_exception { public: \
|
||||
using builtin_exception::builtin_exception; \
|
||||
name() : name("") { } \
|
||||
void set_error() const override { PyErr_SetString(type, what()); } \
|
||||
@@ -727,11 +831,12 @@ PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError)
|
||||
PYBIND11_RUNTIME_EXCEPTION(attribute_error, PyExc_AttributeError)
|
||||
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); }
|
||||
[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const char *reason) { throw std::runtime_error(reason); }
|
||||
[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); }
|
||||
|
||||
template <typename T, typename SFINAE = void> struct format_descriptor { };
|
||||
|
||||
@@ -776,7 +881,8 @@ struct nodelete { template <typename T> void operator()(T*) { } };
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <typename... Args>
|
||||
struct overload_cast_impl {
|
||||
constexpr overload_cast_impl() {}; // NOLINT(modernize-use-equals-default): MSVC 2015 needs this
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default): MSVC 2015 needs this
|
||||
constexpr overload_cast_impl() {}
|
||||
|
||||
template <typename Return>
|
||||
constexpr auto operator()(Return (*pf)(Args...)) const noexcept
|
||||
@@ -832,6 +938,7 @@ public:
|
||||
|
||||
// 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>>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
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
|
||||
@@ -840,9 +947,11 @@ public:
|
||||
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.
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
any_container(std::vector<T> &&v) : v(std::move(v)) { }
|
||||
|
||||
// Moves the vector out of an rvalue any_container
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::vector<T> &&() && { return std::move(v); }
|
||||
|
||||
// Dereferencing obtains a reference to the underlying vector
|
||||
@@ -857,5 +966,57 @@ public:
|
||||
// Forward-declaration; see detail/class.h
|
||||
std::string get_fully_qualified_tp_name(PyTypeObject*);
|
||||
|
||||
template <typename T>
|
||||
inline static std::shared_ptr<T> try_get_shared_from_this(std::enable_shared_from_this<T> *holder_value_ptr) {
|
||||
// Pre C++17, this code path exploits undefined behavior, but is known to work on many platforms.
|
||||
// Use at your own risk!
|
||||
// See also https://en.cppreference.com/w/cpp/memory/enable_shared_from_this, and in particular
|
||||
// the `std::shared_ptr<Good> gp1 = not_so_good.getptr();` and `try`-`catch` parts of the example.
|
||||
#if defined(__cpp_lib_enable_shared_from_this) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
|
||||
return holder_value_ptr->weak_from_this().lock();
|
||||
#else
|
||||
try {
|
||||
return holder_value_ptr->shared_from_this();
|
||||
}
|
||||
catch (const std::bad_weak_ptr &) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// For silencing "unused" compiler warnings in special situations.
|
||||
template <typename... Args>
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER < 1920 // MSVC 2017
|
||||
constexpr
|
||||
#endif
|
||||
inline void silence_unused_warnings(Args &&...) {}
|
||||
|
||||
// MSVC warning C4100: Unreferenced formal parameter
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1916
|
||||
# define PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(...) \
|
||||
detail::silence_unused_warnings(__VA_ARGS__)
|
||||
#else
|
||||
# define PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(...)
|
||||
#endif
|
||||
|
||||
// GCC -Wunused-but-set-parameter All GCC versions (as of July 2021).
|
||||
#if defined(__GNUG__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(...) \
|
||||
detail::silence_unused_warnings(__VA_ARGS__)
|
||||
#else
|
||||
# define PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(...)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) // All versions (as of July 2021).
|
||||
|
||||
// warning C4127: Conditional expression is constant
|
||||
constexpr inline bool silence_msvc_c4127(bool cond) { return cond; }
|
||||
|
||||
# define PYBIND11_SILENCE_MSVC_C4127(...) ::pybind11::detail::silence_msvc_c4127(__VA_ARGS__)
|
||||
|
||||
#else
|
||||
# define PYBIND11_SILENCE_MSVC_C4127(...) __VA_ARGS__
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
@@ -23,15 +23,17 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
/* Concatenate type signatures at compile time */
|
||||
template <size_t N, typename... Ts>
|
||||
struct descr {
|
||||
char text[N + 1];
|
||||
char text[N + 1]{'\0'};
|
||||
|
||||
constexpr descr() : text{'\0'} { }
|
||||
constexpr descr() = default;
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { }
|
||||
|
||||
template <size_t... Is>
|
||||
constexpr descr(char const (&s)[N+1], index_sequence<Is...>) : text{s[Is]..., '\0'} { }
|
||||
|
||||
template <typename... Chars>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} { }
|
||||
|
||||
static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() {
|
||||
@@ -42,6 +44,7 @@ struct descr {
|
||||
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2>
|
||||
constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b,
|
||||
index_sequence<Is1...>, index_sequence<Is2...>) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b);
|
||||
return {a.text[Is1]..., b.text[Is2]...};
|
||||
}
|
||||
|
||||
@@ -74,7 +77,8 @@ constexpr enable_if_t<B, T1> _(const T1 &d, const T2 &) { return d; }
|
||||
template <bool B, typename T1, typename T2>
|
||||
constexpr enable_if_t<!B, T2> _(const T1 &, const T2 &d) { return d; }
|
||||
|
||||
template <size_t Size> auto constexpr _() -> decltype(int_to_str<Size / 10, Size % 10>::digits) {
|
||||
template <size_t Size>
|
||||
auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
|
||||
return int_to_str<Size / 10, Size % 10>::digits;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename> using cast_op_type = value_and_holder &;
|
||||
operator value_and_holder &() { return *value; }
|
||||
explicit operator value_and_holder &() { return *value; }
|
||||
static constexpr auto name = _<value_and_holder>();
|
||||
|
||||
private:
|
||||
@@ -94,8 +94,9 @@ void construct(...) {
|
||||
// construct an Alias from the returned base instance.
|
||||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
no_nullptr(ptr);
|
||||
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) {
|
||||
// We're going to try to construct an alias by moving the cpp type. Whether or not
|
||||
// that succeeds, we still need to destroy the original cpp pointer (either the
|
||||
// moved away leftover, if the alias construction works, or the value itself if we
|
||||
@@ -131,10 +132,11 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
|
||||
// derived type (through those holder's implicit conversion from derived class holder constructors).
|
||||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
auto *ptr = holder_helper<Holder<Class>>::get(holder);
|
||||
no_nullptr(ptr);
|
||||
// If we need an alias, check that the held pointer is actually an alias instance
|
||||
if (Class::has_alias && need_alias && !is_alias<Class>(ptr))
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr))
|
||||
throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance "
|
||||
"is not an alias instance");
|
||||
|
||||
@@ -148,9 +150,10 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
||||
// need it, we simply move-construct the cpp value into a new instance.
|
||||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
static_assert(std::is_move_constructible<Cpp<Class>>::value,
|
||||
"pybind11::init() return-by-value factory function requires a movable class");
|
||||
if (Class::has_alias && need_alias)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias)
|
||||
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
|
||||
else
|
||||
v_h.value_ptr() = new Cpp<Class>(std::move(result));
|
||||
@@ -219,7 +222,8 @@ template <typename Func, typename Return, typename... Args>
|
||||
struct factory<Func, void_type (*)(), Return(Args...)> {
|
||||
remove_reference_t<Func> class_factory;
|
||||
|
||||
factory(Func &&f) : class_factory(std::forward<Func>(f)) { }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
factory(Func &&f) : class_factory(std::forward<Func>(f)) {}
|
||||
|
||||
// The given class either has no alias or has no separate alias factory;
|
||||
// this always constructs the class itself. If the class is registered with an alias
|
||||
@@ -293,7 +297,13 @@ template <typename Class, typename T, typename O,
|
||||
enable_if_t<std::is_convertible<O, handle>::value, int> = 0>
|
||||
void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) {
|
||||
construct<Class>(v_h, std::move(result.first), need_alias);
|
||||
setattr((PyObject *) v_h.inst, "__dict__", result.second);
|
||||
auto d = handle(result.second);
|
||||
if (PyDict_Check(d.ptr()) && PyDict_Size(d.ptr()) == 0) {
|
||||
// Skipping setattr below, to not force use of py::dynamic_attr() for Class unnecessarily.
|
||||
// See PR #2972 for details.
|
||||
return;
|
||||
}
|
||||
setattr((PyObject *) v_h.inst, "__dict__", d);
|
||||
}
|
||||
|
||||
/// Implementation for py::pickle(GetState, SetState)
|
||||
|
||||
@@ -11,8 +11,30 @@
|
||||
|
||||
#include "../pytypes.h"
|
||||
|
||||
/// Tracks the `internals` and `type_info` ABI version independent of the main library version.
|
||||
///
|
||||
/// Some portions of the code use an ABI that is conditional depending on this
|
||||
/// version number. That allows ABI-breaking changes to be "pre-implemented".
|
||||
/// Once the default version number is incremented, the conditional logic that
|
||||
/// no longer applies can be removed. Additionally, users that need not
|
||||
/// maintain ABI compatibility can increase the version number in order to take
|
||||
/// advantage of any functionality/efficiency improvements that depend on the
|
||||
/// newer ABI.
|
||||
///
|
||||
/// WARNING: If you choose to manually increase the ABI version, note that
|
||||
/// pybind11 may not be tested as thoroughly with a non-default ABI version, and
|
||||
/// further ABI-incompatible changes may be made before the ABI is officially
|
||||
/// changed to the new version.
|
||||
#ifndef PYBIND11_INTERNALS_VERSION
|
||||
# define PYBIND11_INTERNALS_VERSION 4
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
using ExceptionTranslator = void (*)(std::exception_ptr);
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// Forward declarations
|
||||
inline PyTypeObject *make_static_property_type();
|
||||
inline PyTypeObject *make_default_metaclass();
|
||||
@@ -21,30 +43,59 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
|
||||
// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new
|
||||
// Thread Specific Storage (TSS) API.
|
||||
#if PY_VERSION_HEX >= 0x03070000
|
||||
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
|
||||
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
|
||||
#else
|
||||
// Usually an int but a long on Cygwin64 with Python 3.x
|
||||
# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key))
|
||||
# if PY_MAJOR_VERSION < 3
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) \
|
||||
PyThread_delete_key_value(key)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
|
||||
do { \
|
||||
PyThread_delete_key_value((key)); \
|
||||
PyThread_set_key_value((key), (value)); \
|
||||
} while (false)
|
||||
// Avoid unnecessary allocation of `Py_tss_t`, since we cannot use
|
||||
// `Py_LIMITED_API` anyway.
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
# define PYBIND11_TLS_KEY_REF Py_tss_t &
|
||||
# ifdef __GNUC__
|
||||
// Clang on macOS warns due to `Py_tss_NEEDS_INIT` not specifying an initializer
|
||||
// for every field.
|
||||
# define PYBIND11_TLS_KEY_INIT(var) \
|
||||
_Pragma("GCC diagnostic push") /**/ \
|
||||
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
|
||||
Py_tss_t var \
|
||||
= Py_tss_NEEDS_INIT; \
|
||||
_Pragma("GCC diagnostic pop")
|
||||
# else
|
||||
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
|
||||
# endif
|
||||
# define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0)
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key))
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value))
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr)
|
||||
# define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key))
|
||||
# else
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) \
|
||||
PyThread_set_key_value((key), nullptr)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
|
||||
PyThread_set_key_value((key), (value))
|
||||
# define PYBIND11_TLS_KEY_REF Py_tss_t *
|
||||
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr;
|
||||
# define PYBIND11_TLS_KEY_CREATE(var) \
|
||||
(((var) = PyThread_tss_alloc()) != nullptr && (PyThread_tss_create((var)) == 0))
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
|
||||
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
|
||||
# endif
|
||||
# define PYBIND11_TLS_FREE(key) (void)key
|
||||
#else
|
||||
// Usually an int but a long on Cygwin64 with Python 3.x
|
||||
# define PYBIND11_TLS_KEY_REF decltype(PyThread_create_key())
|
||||
# define PYBIND11_TLS_KEY_INIT(var) PYBIND11_TLS_KEY_REF var = 0;
|
||||
# define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1)
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key))
|
||||
# if PY_MAJOR_VERSION < 3 || defined(PYPY_VERSION)
|
||||
// On CPython < 3.4 and on PyPy, `PyThread_set_key_value` strangely does not set
|
||||
// the value if it has already been set. Instead, it must first be deleted and
|
||||
// then set again.
|
||||
inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) {
|
||||
PyThread_delete_key_value(key);
|
||||
PyThread_set_key_value(key, value);
|
||||
}
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_delete_key_value(key)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
|
||||
::pybind11::detail::tls_replace_value((key), (value))
|
||||
# else
|
||||
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_set_key_value((key), nullptr)
|
||||
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_set_key_value((key), (value))
|
||||
# endif
|
||||
# define PYBIND11_TLS_FREE(key) (void) key
|
||||
#endif
|
||||
|
||||
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
|
||||
@@ -100,24 +151,33 @@ struct internals {
|
||||
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash> inactive_override_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::forward_list<ExceptionTranslator> 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`
|
||||
#if PYBIND11_INTERNALS_VERSION == 4
|
||||
std::vector<PyObject *> unused_loader_patient_stack_remove_at_v5;
|
||||
#endif
|
||||
std::forward_list<std::string> static_strings; // Stores the std::strings backing detail::c_str()
|
||||
PyTypeObject *static_property_type;
|
||||
PyTypeObject *default_metaclass;
|
||||
PyObject *instance_base;
|
||||
#if defined(WITH_THREAD)
|
||||
PYBIND11_TLS_KEY_INIT(tstate);
|
||||
PYBIND11_TLS_KEY_INIT(tstate)
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||
# endif // PYBIND11_INTERNALS_VERSION > 4
|
||||
PyInterpreterState *istate = nullptr;
|
||||
~internals() {
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
PYBIND11_TLS_FREE(loader_life_support_tls_key);
|
||||
# endif // PYBIND11_INTERNALS_VERSION > 4
|
||||
|
||||
// This destructor is called *after* Py_Finalize() in finalize_interpreter().
|
||||
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is called.
|
||||
// PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing.
|
||||
// PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
|
||||
// PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither
|
||||
// of those have anything to do with CPython internals.
|
||||
// PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator.
|
||||
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is
|
||||
// called. PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does
|
||||
// nothing. PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
|
||||
// PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX).
|
||||
// Neither of those have anything to do with CPython internals. PyMem_RawFree *requires*
|
||||
// that the `tstate` be allocated with the CPython allocator.
|
||||
PYBIND11_TLS_FREE(tstate);
|
||||
}
|
||||
#endif
|
||||
@@ -149,9 +209,6 @@ struct type_info {
|
||||
bool module_local : 1;
|
||||
};
|
||||
|
||||
/// Tracks the `internals` and `type_info` ABI version independent of the main library version
|
||||
#define PYBIND11_INTERNALS_VERSION 4
|
||||
|
||||
/// On MSVC, debug and release builds are not ABI-compatible!
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
@@ -253,7 +310,7 @@ inline void translate_local_exception(std::exception_ptr p) {
|
||||
#endif
|
||||
|
||||
/// Return a reference to the current `internals` data
|
||||
PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
PYBIND11_NOINLINE internals &get_internals() {
|
||||
auto **&internals_pp = get_internals_pp();
|
||||
if (internals_pp && *internals_pp)
|
||||
return **internals_pp;
|
||||
@@ -276,6 +333,8 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
// initial exception translator, below, so add another for our local exception classes.
|
||||
//
|
||||
// libstdc++ doesn't require this (types there are identified only by name)
|
||||
// libc++ with CPython doesn't require this (types are explicitly exported)
|
||||
// libc++ with PyPy still need it, awaiting further investigation
|
||||
#if !defined(__GLIBCXX__)
|
||||
(*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
|
||||
#endif
|
||||
@@ -285,21 +344,21 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
internals_ptr = new internals();
|
||||
#if defined(WITH_THREAD)
|
||||
|
||||
#if PY_VERSION_HEX < 0x03090000
|
||||
PyEval_InitThreads();
|
||||
#endif
|
||||
# if PY_VERSION_HEX < 0x03090000
|
||||
PyEval_InitThreads();
|
||||
# endif
|
||||
PyThreadState *tstate = PyThreadState_Get();
|
||||
#if PY_VERSION_HEX >= 0x03070000
|
||||
internals_ptr->tstate = PyThread_tss_alloc();
|
||||
if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate))
|
||||
pybind11_fail("get_internals: could not successfully initialize the TSS key!");
|
||||
PyThread_tss_set(internals_ptr->tstate, tstate);
|
||||
#else
|
||||
internals_ptr->tstate = PyThread_create_key();
|
||||
if (internals_ptr->tstate == -1)
|
||||
pybind11_fail("get_internals: could not successfully initialize the TLS key!");
|
||||
PyThread_set_key_value(internals_ptr->tstate, tstate);
|
||||
#endif
|
||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
|
||||
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
|
||||
}
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
|
||||
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
|
||||
pybind11_fail("get_internals: could not successfully initialize the "
|
||||
"loader_life_support TSS key!");
|
||||
}
|
||||
# endif
|
||||
internals_ptr->istate = tstate->interp;
|
||||
#endif
|
||||
builtins[id] = capsule(internals_pp);
|
||||
@@ -311,12 +370,57 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
||||
return **internals_pp;
|
||||
}
|
||||
|
||||
/// Works like `internals.registered_types_cpp`, but for module-local registered types:
|
||||
inline type_map<type_info *> ®istered_local_types_cpp() {
|
||||
static type_map<type_info *> locals{};
|
||||
return locals;
|
||||
// the internals struct (above) is shared between all the modules. local_internals are only
|
||||
// for a single module. Any changes made to internals may require an update to
|
||||
// PYBIND11_INTERNALS_VERSION, breaking backwards compatibility. local_internals is, by design,
|
||||
// restricted to a single module. Whether a module has local internals or not should not
|
||||
// impact any other modules, because the only things accessing the local internals is the
|
||||
// module that contains them.
|
||||
struct local_internals {
|
||||
type_map<type_info *> registered_types_cpp;
|
||||
std::forward_list<ExceptionTranslator> registered_exception_translators;
|
||||
#if defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
|
||||
|
||||
// For ABI compatibility, we can't store the loader_life_support TLS key in
|
||||
// the `internals` struct directly. Instead, we store it in `shared_data` and
|
||||
// cache a copy in `local_internals`. If we allocated a separate TLS key for
|
||||
// each instance of `local_internals`, we could end up allocating hundreds of
|
||||
// TLS keys if hundreds of different pybind11 modules are loaded (which is a
|
||||
// plausible number).
|
||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||
|
||||
// Holds the shared TLS key for the loader_life_support stack.
|
||||
struct shared_loader_life_support_data {
|
||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||
shared_loader_life_support_data() {
|
||||
if (!PYBIND11_TLS_KEY_CREATE(loader_life_support_tls_key)) {
|
||||
pybind11_fail("local_internals: could not successfully initialize the "
|
||||
"loader_life_support TLS key!");
|
||||
}
|
||||
}
|
||||
// We can't help but leak the TLS key, because Python never unloads extension modules.
|
||||
};
|
||||
|
||||
local_internals() {
|
||||
auto &internals = get_internals();
|
||||
// Get or create the `loader_life_support_stack_key`.
|
||||
auto &ptr = internals.shared_data["_life_support"];
|
||||
if (!ptr) {
|
||||
ptr = new shared_loader_life_support_data;
|
||||
}
|
||||
loader_life_support_tls_key
|
||||
= static_cast<shared_loader_life_support_data *>(ptr)->loader_life_support_tls_key;
|
||||
}
|
||||
#endif // defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
|
||||
};
|
||||
|
||||
/// Works like `get_internals`, but for things which are locally registered.
|
||||
inline local_internals &get_local_internals() {
|
||||
static local_internals locals;
|
||||
return locals;
|
||||
}
|
||||
|
||||
|
||||
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
|
||||
/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only
|
||||
/// cleared when the program exits or after interpreter shutdown (when embedding), and so are
|
||||
@@ -333,14 +437,14 @@ PYBIND11_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) {
|
||||
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) {
|
||||
PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
|
||||
detail::get_internals().shared_data[name] = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
985
ext/pybind11/include/pybind11/detail/type_caster_base.h
Normal file
985
ext/pybind11/include/pybind11/detail/type_caster_base.h
Normal file
@@ -0,0 +1,985 @@
|
||||
/*
|
||||
pybind11/detail/type_caster_base.h (originally first part of pybind11/cast.h)
|
||||
|
||||
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 "../pytypes.h"
|
||||
#include "common.h"
|
||||
#include "descr.h"
|
||||
#include "internals.h"
|
||||
#include "typeid.h"
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// A life support system for temporary objects created by `type_caster::load()`.
|
||||
/// Adding a patient will keep it alive up until the enclosing function returns.
|
||||
class loader_life_support {
|
||||
private:
|
||||
loader_life_support* parent = nullptr;
|
||||
std::unordered_set<PyObject *> keep_alive;
|
||||
|
||||
#if defined(WITH_THREAD)
|
||||
// Store stack pointer in thread-local storage.
|
||||
static PYBIND11_TLS_KEY_REF get_stack_tls_key() {
|
||||
# if PYBIND11_INTERNALS_VERSION == 4
|
||||
return get_local_internals().loader_life_support_tls_key;
|
||||
# else
|
||||
return get_internals().loader_life_support_tls_key;
|
||||
# endif
|
||||
}
|
||||
static loader_life_support *get_stack_top() {
|
||||
return static_cast<loader_life_support *>(PYBIND11_TLS_GET_VALUE(get_stack_tls_key()));
|
||||
}
|
||||
static void set_stack_top(loader_life_support *value) {
|
||||
PYBIND11_TLS_REPLACE_VALUE(get_stack_tls_key(), value);
|
||||
}
|
||||
#else
|
||||
// Use single global variable for stack.
|
||||
static loader_life_support **get_stack_pp() {
|
||||
static loader_life_support *global_stack = nullptr;
|
||||
return global_stack;
|
||||
}
|
||||
static loader_life_support *get_stack_top() { return *get_stack_pp(); }
|
||||
static void set_stack_top(loader_life_support *value) { *get_stack_pp() = value; }
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// A new patient frame is created when a function is entered
|
||||
loader_life_support() {
|
||||
parent = get_stack_top();
|
||||
set_stack_top(this);
|
||||
}
|
||||
|
||||
/// ... and destroyed after it returns
|
||||
~loader_life_support() {
|
||||
if (get_stack_top() != this)
|
||||
pybind11_fail("loader_life_support: internal error");
|
||||
set_stack_top(parent);
|
||||
for (auto* item : keep_alive)
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
||||
/// This can only be used inside a pybind11-bound function, either by `argument_loader`
|
||||
/// at argument preparation time or by `py::cast()` at execution time.
|
||||
PYBIND11_NOINLINE static void add_patient(handle h) {
|
||||
loader_life_support *frame = get_stack_top();
|
||||
if (!frame) {
|
||||
// NOTE: It would be nice to include the stack frames here, as this indicates
|
||||
// use of pybind11::cast<> outside the normal call framework, finding such
|
||||
// a location is challenging. Developers could consider printing out
|
||||
// stack frame addresses here using something like __builtin_frame_address(0)
|
||||
throw cast_error("When called outside a bound function, py::cast() cannot "
|
||||
"do Python -> C++ conversions which require the creation "
|
||||
"of temporary values");
|
||||
}
|
||||
|
||||
if (frame->keep_alive.insert(h.ptr()).second)
|
||||
Py_INCREF(h.ptr());
|
||||
}
|
||||
};
|
||||
|
||||
// Gets the cache entry for the given type, creating it if necessary. The return value is the pair
|
||||
// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was
|
||||
// just created.
|
||||
inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_type_info_get_cache(PyTypeObject *type);
|
||||
|
||||
// Populates a just-created cache entry.
|
||||
PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) {
|
||||
std::vector<PyTypeObject *> check;
|
||||
for (handle parent : reinterpret_borrow<tuple>(t->tp_bases))
|
||||
check.push_back((PyTypeObject *) parent.ptr());
|
||||
|
||||
auto const &type_dict = get_internals().registered_types_py;
|
||||
for (size_t i = 0; i < check.size(); i++) {
|
||||
auto type = check[i];
|
||||
// Ignore Python2 old-style class super type:
|
||||
if (!PyType_Check((PyObject *) type)) continue;
|
||||
|
||||
// Check `type` in the current set of registered python types:
|
||||
auto it = type_dict.find(type);
|
||||
if (it != type_dict.end()) {
|
||||
// We found a cache entry for it, so it's either pybind-registered or has pre-computed
|
||||
// pybind bases, but we have to make sure we haven't already seen the type(s) before: we
|
||||
// want to follow Python/virtual C++ rules that there should only be one instance of a
|
||||
// common base.
|
||||
for (auto *tinfo : it->second) {
|
||||
// NB: Could use a second set here, rather than doing a linear search, but since
|
||||
// having a large number of immediate pybind11-registered types seems fairly
|
||||
// unlikely, that probably isn't worthwhile.
|
||||
bool found = false;
|
||||
for (auto *known : bases) {
|
||||
if (known == tinfo) { found = true; break; }
|
||||
}
|
||||
if (!found) bases.push_back(tinfo);
|
||||
}
|
||||
}
|
||||
else if (type->tp_bases) {
|
||||
// It's some python type, so keep follow its bases classes to look for one or more
|
||||
// registered types
|
||||
if (i + 1 == check.size()) {
|
||||
// When we're at the end, we can pop off the current element to avoid growing
|
||||
// `check` when adding just one base (which is typical--i.e. when there is no
|
||||
// multiple inheritance)
|
||||
check.pop_back();
|
||||
i--;
|
||||
}
|
||||
for (handle parent : reinterpret_borrow<tuple>(type->tp_bases))
|
||||
check.push_back((PyTypeObject *) parent.ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will
|
||||
* be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side
|
||||
* derived class that uses single inheritance. Will contain as many types as required for a Python
|
||||
* class that uses multiple inheritance to inherit (directly or indirectly) from multiple
|
||||
* pybind-registered classes. Will be empty if neither the type nor any base classes are
|
||||
* pybind-registered.
|
||||
*
|
||||
* The value is cached for the lifetime of the Python type.
|
||||
*/
|
||||
inline const std::vector<detail::type_info *> &all_type_info(PyTypeObject *type) {
|
||||
auto ins = all_type_info_get_cache(type);
|
||||
if (ins.second)
|
||||
// New cache entry: populate it
|
||||
all_type_info_populate(type, ins.first->second);
|
||||
|
||||
return ins.first->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any
|
||||
* ancestors are pybind11-registered. Throws an exception if there are multiple bases--use
|
||||
* `all_type_info` instead if you want to support multiple bases.
|
||||
*/
|
||||
PYBIND11_NOINLINE detail::type_info* get_type_info(PyTypeObject *type) {
|
||||
auto &bases = all_type_info(type);
|
||||
if (bases.empty())
|
||||
return nullptr;
|
||||
if (bases.size() > 1)
|
||||
pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases");
|
||||
return bases.front();
|
||||
}
|
||||
|
||||
inline detail::type_info *get_local_type_info(const std::type_index &tp) {
|
||||
auto &locals = get_local_internals().registered_types_cpp;
|
||||
auto it = locals.find(tp);
|
||||
if (it != locals.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline detail::type_info *get_global_type_info(const std::type_index &tp) {
|
||||
auto &types = get_internals().registered_types_cpp;
|
||||
auto it = types.find(tp);
|
||||
if (it != types.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr.
|
||||
PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_index &tp,
|
||||
bool throw_if_missing = false) {
|
||||
if (auto ltype = get_local_type_info(tp))
|
||||
return ltype;
|
||||
if (auto gtype = get_global_type_info(tp))
|
||||
return gtype;
|
||||
|
||||
if (throw_if_missing) {
|
||||
std::string tname = tp.name();
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\"");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE handle get_type_handle(const std::type_info &tp, bool throw_if_missing) {
|
||||
detail::type_info *type_info = get_type_info(tp, throw_if_missing);
|
||||
return handle(type_info ? ((PyObject *) type_info->type) : nullptr);
|
||||
}
|
||||
|
||||
// Searches the inheritance graph for a registered Python instance, using all_type_info().
|
||||
PYBIND11_NOINLINE handle find_registered_python_instance(void *src,
|
||||
const detail::type_info *tinfo) {
|
||||
auto it_instances = get_internals().registered_instances.equal_range(src);
|
||||
for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
|
||||
for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
|
||||
if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype))
|
||||
return handle((PyObject *) it_i->second).inc_ref();
|
||||
}
|
||||
}
|
||||
return handle();
|
||||
}
|
||||
|
||||
struct value_and_holder {
|
||||
instance *inst = nullptr;
|
||||
size_t index = 0u;
|
||||
const detail::type_info *type = nullptr;
|
||||
void **vh = nullptr;
|
||||
|
||||
// Main constructor for a found value/holder:
|
||||
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) :
|
||||
inst{i}, index{index}, type{type},
|
||||
vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]}
|
||||
{}
|
||||
|
||||
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
|
||||
value_and_holder() = default;
|
||||
|
||||
// Used for past-the-end iterator
|
||||
explicit value_and_holder(size_t index) : index{index} {}
|
||||
|
||||
template <typename V = void> V *&value_ptr() const {
|
||||
return reinterpret_cast<V *&>(vh[0]);
|
||||
}
|
||||
// True if this `value_and_holder` has a non-null value pointer
|
||||
explicit operator bool() const { return value_ptr() != nullptr; }
|
||||
|
||||
template <typename H> H &holder() const {
|
||||
return reinterpret_cast<H &>(vh[1]);
|
||||
}
|
||||
bool holder_constructed() const {
|
||||
return inst->simple_layout
|
||||
? inst->simple_holder_constructed
|
||||
: (inst->nonsimple.status[index] & instance::status_holder_constructed) != 0u;
|
||||
}
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
void set_holder_constructed(bool v = true) {
|
||||
if (inst->simple_layout)
|
||||
inst->simple_holder_constructed = v;
|
||||
else if (v)
|
||||
inst->nonsimple.status[index] |= instance::status_holder_constructed;
|
||||
else
|
||||
inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_holder_constructed;
|
||||
}
|
||||
bool instance_registered() const {
|
||||
return inst->simple_layout
|
||||
? inst->simple_instance_registered
|
||||
: ((inst->nonsimple.status[index] & instance::status_instance_registered) != 0);
|
||||
}
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
void set_instance_registered(bool v = true) {
|
||||
if (inst->simple_layout)
|
||||
inst->simple_instance_registered = v;
|
||||
else if (v)
|
||||
inst->nonsimple.status[index] |= instance::status_instance_registered;
|
||||
else
|
||||
inst->nonsimple.status[index] &= (std::uint8_t) ~instance::status_instance_registered;
|
||||
}
|
||||
};
|
||||
|
||||
// Container for accessing and iterating over an instance's values/holders
|
||||
struct values_and_holders {
|
||||
private:
|
||||
instance *inst;
|
||||
using type_vec = std::vector<detail::type_info *>;
|
||||
const type_vec &tinfo;
|
||||
|
||||
public:
|
||||
explicit values_and_holders(instance *inst)
|
||||
: inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {}
|
||||
|
||||
struct iterator {
|
||||
private:
|
||||
instance *inst = nullptr;
|
||||
const type_vec *types = nullptr;
|
||||
value_and_holder curr;
|
||||
friend struct values_and_holders;
|
||||
iterator(instance *inst, const type_vec *tinfo)
|
||||
: inst{inst}, types{tinfo},
|
||||
curr(inst /* instance */,
|
||||
types->empty() ? nullptr : (*types)[0] /* type info */,
|
||||
0, /* vpos: (non-simple types only): the first vptr comes first */
|
||||
0 /* index */)
|
||||
{}
|
||||
// Past-the-end iterator:
|
||||
explicit iterator(size_t end) : curr(end) {}
|
||||
|
||||
public:
|
||||
bool operator==(const iterator &other) const { return curr.index == other.curr.index; }
|
||||
bool operator!=(const iterator &other) const { return curr.index != other.curr.index; }
|
||||
iterator &operator++() {
|
||||
if (!inst->simple_layout)
|
||||
curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs;
|
||||
++curr.index;
|
||||
curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr;
|
||||
return *this;
|
||||
}
|
||||
value_and_holder &operator*() { return curr; }
|
||||
value_and_holder *operator->() { return &curr; }
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(inst, &tinfo); }
|
||||
iterator end() { return iterator(tinfo.size()); }
|
||||
|
||||
iterator find(const type_info *find_type) {
|
||||
auto it = begin(), endit = end();
|
||||
while (it != endit && it->type != find_type) ++it;
|
||||
return it;
|
||||
}
|
||||
|
||||
size_t size() { return tinfo.size(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts C++ value and holder pointer references from an instance (which may contain multiple
|
||||
* values/holders for python-side multiple inheritance) that match the given type. Throws an error
|
||||
* if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If
|
||||
* `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned,
|
||||
* regardless of type (and the resulting .type will be nullptr).
|
||||
*
|
||||
* The returned object should be short-lived: in particular, it must not outlive the called-upon
|
||||
* instance.
|
||||
*/
|
||||
PYBIND11_NOINLINE value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) {
|
||||
// Optimize common case:
|
||||
if (!find_type || Py_TYPE(this) == find_type->type)
|
||||
return value_and_holder(this, find_type, 0, 0);
|
||||
|
||||
detail::values_and_holders vhs(this);
|
||||
auto it = vhs.find(find_type);
|
||||
if (it != vhs.end())
|
||||
return *it;
|
||||
|
||||
if (!throw_if_missing)
|
||||
return value_and_holder();
|
||||
|
||||
#if defined(NDEBUG)
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: "
|
||||
"type is not a pybind11 base of the given instance "
|
||||
"(compile in debug mode for type details)");
|
||||
#else
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" +
|
||||
get_fully_qualified_tp_name(find_type->type) + "' is not a pybind11 base of the given `" +
|
||||
get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance");
|
||||
#endif
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE void instance::allocate_layout() {
|
||||
auto &tinfo = all_type_info(Py_TYPE(this));
|
||||
|
||||
const size_t n_types = tinfo.size();
|
||||
|
||||
if (n_types == 0)
|
||||
pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types");
|
||||
|
||||
simple_layout =
|
||||
n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs();
|
||||
|
||||
// Simple path: no python-side multiple inheritance, and a small-enough holder
|
||||
if (simple_layout) {
|
||||
simple_value_holder[0] = nullptr;
|
||||
simple_holder_constructed = false;
|
||||
simple_instance_registered = false;
|
||||
}
|
||||
else { // multiple base types or a too-large holder
|
||||
// Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer,
|
||||
// [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool
|
||||
// values that tracks whether each associated holder has been initialized. Each [block] is
|
||||
// padded, if necessary, to an integer multiple of sizeof(void *).
|
||||
size_t space = 0;
|
||||
for (auto t : tinfo) {
|
||||
space += 1; // value pointer
|
||||
space += t->holder_size_in_ptrs; // holder instance
|
||||
}
|
||||
size_t flags_at = space;
|
||||
space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered)
|
||||
|
||||
// Allocate space for flags, values, and holders, and initialize it to 0 (flags and values,
|
||||
// in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6
|
||||
// they default to using pymalloc, which is designed to be efficient for small allocations
|
||||
// like the one we're doing here; in earlier versions (and for larger allocations) they are
|
||||
// just wrappers around malloc.
|
||||
#if PY_VERSION_HEX >= 0x03050000
|
||||
nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *));
|
||||
if (!nonsimple.values_and_holders) throw std::bad_alloc();
|
||||
#else
|
||||
nonsimple.values_and_holders = (void **) PyMem_New(void *, space);
|
||||
if (!nonsimple.values_and_holders) throw std::bad_alloc();
|
||||
std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *));
|
||||
#endif
|
||||
nonsimple.status = reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]);
|
||||
}
|
||||
owned = true;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
PYBIND11_NOINLINE void instance::deallocate_layout() {
|
||||
if (!simple_layout)
|
||||
PyMem_Free(nonsimple.values_and_holders);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp) {
|
||||
handle type = detail::get_type_handle(tp, false);
|
||||
if (!type)
|
||||
return false;
|
||||
return isinstance(obj, type);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE std::string error_string() {
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred");
|
||||
return "Unknown internal error occurred";
|
||||
}
|
||||
|
||||
error_scope scope; // Preserve error state
|
||||
|
||||
std::string errorString;
|
||||
if (scope.type) {
|
||||
errorString += handle(scope.type).attr("__name__").cast<std::string>();
|
||||
errorString += ": ";
|
||||
}
|
||||
if (scope.value)
|
||||
errorString += (std::string) str(scope.value);
|
||||
|
||||
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if (scope.trace != nullptr)
|
||||
PyException_SetTraceback(scope.value, scope.trace);
|
||||
#endif
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
if (scope.trace) {
|
||||
auto *trace = (PyTracebackObject *) scope.trace;
|
||||
|
||||
/* Get the deepest trace possible */
|
||||
while (trace->tb_next)
|
||||
trace = trace->tb_next;
|
||||
|
||||
PyFrameObject *frame = trace->tb_frame;
|
||||
errorString += "\n\nAt:\n";
|
||||
while (frame) {
|
||||
#if PY_VERSION_HEX >= 0x03090000
|
||||
PyCodeObject *f_code = PyFrame_GetCode(frame);
|
||||
#else
|
||||
PyCodeObject *f_code = frame->f_code;
|
||||
Py_INCREF(f_code);
|
||||
#endif
|
||||
int lineno = PyFrame_GetLineNumber(frame);
|
||||
errorString +=
|
||||
" " + handle(f_code->co_filename).cast<std::string>() +
|
||||
"(" + std::to_string(lineno) + "): " +
|
||||
handle(f_code->co_name).cast<std::string>() + "\n";
|
||||
frame = frame->f_back;
|
||||
Py_DECREF(f_code);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return errorString;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type ) {
|
||||
auto &instances = get_internals().registered_instances;
|
||||
auto range = instances.equal_range(ptr);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
for (const auto &vh : values_and_holders(it->second)) {
|
||||
if (vh.type == type)
|
||||
return handle((PyObject *) it->second);
|
||||
}
|
||||
}
|
||||
return handle();
|
||||
}
|
||||
|
||||
inline PyThreadState *get_thread_state_unchecked() {
|
||||
#if defined(PYPY_VERSION)
|
||||
return PyThreadState_GET();
|
||||
#elif PY_VERSION_HEX < 0x03000000
|
||||
return _PyThreadState_Current;
|
||||
#elif PY_VERSION_HEX < 0x03050000
|
||||
return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||
#elif PY_VERSION_HEX < 0x03050200
|
||||
return (PyThreadState*) _PyThreadState_Current.value;
|
||||
#else
|
||||
return _PyThreadState_UncheckedGet();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Forward declarations
|
||||
void keep_alive_impl(handle nurse, handle patient);
|
||||
inline PyObject *make_new_instance(PyTypeObject *type);
|
||||
|
||||
class type_caster_generic {
|
||||
public:
|
||||
PYBIND11_NOINLINE explicit type_caster_generic(const std::type_info &type_info)
|
||||
: typeinfo(get_type_info(type_info)), cpptype(&type_info) {}
|
||||
|
||||
explicit type_caster_generic(const type_info *typeinfo)
|
||||
: typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) {}
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
return load_impl<type_caster_generic>(src, convert);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent,
|
||||
const detail::type_info *tinfo,
|
||||
void *(*copy_constructor)(const void *),
|
||||
void *(*move_constructor)(const void *),
|
||||
const void *existing_holder = nullptr) {
|
||||
if (!tinfo) // no type info: error will be set already
|
||||
return handle();
|
||||
|
||||
void *src = const_cast<void *>(_src);
|
||||
if (src == nullptr)
|
||||
return none().release();
|
||||
|
||||
if (handle registered_inst = find_registered_python_instance(src, tinfo))
|
||||
return registered_inst;
|
||||
|
||||
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
|
||||
auto wrapper = reinterpret_cast<instance *>(inst.ptr());
|
||||
wrapper->owned = false;
|
||||
void *&valueptr = values_and_holders(wrapper).begin()->value_ptr();
|
||||
|
||||
switch (policy) {
|
||||
case return_value_policy::automatic:
|
||||
case return_value_policy::take_ownership:
|
||||
valueptr = src;
|
||||
wrapper->owned = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::automatic_reference:
|
||||
case return_value_policy::reference:
|
||||
valueptr = src;
|
||||
wrapper->owned = false;
|
||||
break;
|
||||
|
||||
case return_value_policy::copy:
|
||||
if (copy_constructor)
|
||||
valueptr = copy_constructor(src);
|
||||
else {
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error("return_value_policy = copy, but type is "
|
||||
"non-copyable! (compile in debug mode for details)");
|
||||
#else
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = copy, but type " +
|
||||
type_name + " is non-copyable!");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::move:
|
||||
if (move_constructor)
|
||||
valueptr = move_constructor(src);
|
||||
else if (copy_constructor)
|
||||
valueptr = copy_constructor(src);
|
||||
else {
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error("return_value_policy = move, but type is neither "
|
||||
"movable nor copyable! "
|
||||
"(compile in debug mode for details)");
|
||||
#else
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = move, but type " +
|
||||
type_name + " is neither movable nor copyable!");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::reference_internal:
|
||||
valueptr = src;
|
||||
wrapper->owned = false;
|
||||
keep_alive_impl(inst, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw cast_error("unhandled return_value_policy: should not happen!");
|
||||
}
|
||||
|
||||
tinfo->init_instance(wrapper, existing_holder);
|
||||
|
||||
return inst.release();
|
||||
}
|
||||
|
||||
// Base methods for generic caster; there are overridden in copyable_holder_caster
|
||||
void load_value(value_and_holder &&v_h) {
|
||||
auto *&vptr = v_h.value_ptr();
|
||||
// Lazy allocation for unallocated values:
|
||||
if (vptr == nullptr) {
|
||||
auto *type = v_h.type ? v_h.type : typeinfo;
|
||||
if (type->operator_new) {
|
||||
vptr = type->operator_new(type->type_size);
|
||||
} else {
|
||||
#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
|
||||
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
vptr = ::operator new(type->type_size,
|
||||
std::align_val_t(type->type_align));
|
||||
else
|
||||
#endif
|
||||
vptr = ::operator new(type->type_size);
|
||||
}
|
||||
}
|
||||
value = vptr;
|
||||
}
|
||||
bool try_implicit_casts(handle src, bool convert) {
|
||||
for (auto &cast : typeinfo->implicit_casts) {
|
||||
type_caster_generic sub_caster(*cast.first);
|
||||
if (sub_caster.load(src, convert)) {
|
||||
value = cast.second(sub_caster.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool try_direct_conversions(handle src) {
|
||||
for (auto &converter : *typeinfo->direct_conversions) {
|
||||
if (converter(src.ptr(), value))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void check_holder_compat() {}
|
||||
|
||||
PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) {
|
||||
auto caster = type_caster_generic(ti);
|
||||
if (caster.load(src, false))
|
||||
return caster.value;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Try to load with foreign typeinfo, if available. Used when there is no
|
||||
/// native typeinfo, or when the native one wasn't able to produce a value.
|
||||
PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) {
|
||||
constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID;
|
||||
const auto pytype = type::handle_of(src);
|
||||
if (!hasattr(pytype, local_key))
|
||||
return false;
|
||||
|
||||
type_info *foreign_typeinfo = reinterpret_borrow<capsule>(getattr(pytype, local_key));
|
||||
// Only consider this foreign loader if actually foreign and is a loader of the correct cpp type
|
||||
if (foreign_typeinfo->module_local_load == &local_load
|
||||
|| (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype)))
|
||||
return false;
|
||||
|
||||
if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) {
|
||||
value = result;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant
|
||||
// bits of code between here and copyable_holder_caster where the two classes need different
|
||||
// logic (without having to resort to virtual inheritance).
|
||||
template <typename ThisT>
|
||||
PYBIND11_NOINLINE bool load_impl(handle src, bool convert) {
|
||||
if (!src) return false;
|
||||
if (!typeinfo) return try_load_foreign_module_local(src);
|
||||
|
||||
auto &this_ = static_cast<ThisT &>(*this);
|
||||
this_.check_holder_compat();
|
||||
|
||||
PyTypeObject *srctype = Py_TYPE(src.ptr());
|
||||
|
||||
// Case 1: If src is an exact type match for the target type then we can reinterpret_cast
|
||||
// the instance's value pointer to the target type:
|
||||
if (srctype == typeinfo->type) {
|
||||
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
|
||||
return true;
|
||||
}
|
||||
// Case 2: We have a derived class
|
||||
if (PyType_IsSubtype(srctype, typeinfo->type)) {
|
||||
auto &bases = all_type_info(srctype);
|
||||
bool no_cpp_mi = typeinfo->simple_type;
|
||||
|
||||
// Case 2a: the python type is a Python-inherited derived class that inherits from just
|
||||
// one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of
|
||||
// the right type and we can use reinterpret_cast.
|
||||
// (This is essentially the same as case 2b, but because not using multiple inheritance
|
||||
// is extremely common, we handle it specially to avoid the loop iterator and type
|
||||
// pointer lookup overhead)
|
||||
if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) {
|
||||
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
|
||||
return true;
|
||||
}
|
||||
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if
|
||||
// we can find an exact match (or, for a simple C++ type, an inherited match); if so, we
|
||||
// can safely reinterpret_cast to the relevant pointer.
|
||||
if (bases.size() > 1) {
|
||||
for (auto base : bases) {
|
||||
if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) {
|
||||
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match
|
||||
// in the registered bases, above, so try implicit casting (needed for proper C++ casting
|
||||
// when MI is involved).
|
||||
if (this_.try_implicit_casts(src, convert))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Perform an implicit conversion
|
||||
if (convert) {
|
||||
for (auto &converter : typeinfo->implicit_conversions) {
|
||||
auto temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
|
||||
if (load_impl<ThisT>(temp, false)) {
|
||||
loader_life_support::add_patient(temp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this_.try_direct_conversions(src))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed to match local typeinfo. Try again with global.
|
||||
if (typeinfo->module_local) {
|
||||
if (auto gtype = get_global_type_info(*typeinfo->cpptype)) {
|
||||
typeinfo = gtype;
|
||||
return load(src, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Global typeinfo has precedence over foreign module_local
|
||||
if (try_load_foreign_module_local(src)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Custom converters didn't take None, now we convert None to nullptr.
|
||||
if (src.is_none()) {
|
||||
// Defer accepting None to other overloads (if we aren't in convert mode):
|
||||
if (!convert) return false;
|
||||
value = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast
|
||||
// isn't needed or can't be used. If the type is unknown, sets the error and returns a pair
|
||||
// with .second = nullptr. (p.first = nullptr is not an error: it becomes None).
|
||||
PYBIND11_NOINLINE static std::pair<const void *, const type_info *> src_and_type(
|
||||
const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) {
|
||||
if (auto *tpi = get_type_info(cast_type))
|
||||
return {src, const_cast<const type_info *>(tpi)};
|
||||
|
||||
// Not found, set error:
|
||||
std::string tname = rtti_type ? rtti_type->name() : cast_type.name();
|
||||
detail::clean_type_id(tname);
|
||||
std::string msg = "Unregistered type : " + tname;
|
||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
const type_info *typeinfo = nullptr;
|
||||
const std::type_info *cpptype = nullptr;
|
||||
void *value = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster
|
||||
* needs to provide `operator T*()` and `operator T&()` operators.
|
||||
*
|
||||
* If the type supports moving the value away via an `operator T&&() &&` method, it should use
|
||||
* `movable_cast_op_type` instead.
|
||||
*/
|
||||
template <typename T>
|
||||
using cast_op_type =
|
||||
conditional_t<std::is_pointer<remove_reference_t<T>>::value,
|
||||
typename std::add_pointer<intrinsic_t<T>>::type,
|
||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>;
|
||||
|
||||
/**
|
||||
* Determine suitable casting operator for a type caster with a movable value. Such a type caster
|
||||
* needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be
|
||||
* called in appropriate contexts where the value can be moved rather than copied.
|
||||
*
|
||||
* These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro.
|
||||
*/
|
||||
template <typename T>
|
||||
using movable_cast_op_type =
|
||||
conditional_t<std::is_pointer<typename std::remove_reference<T>::type>::value,
|
||||
typename std::add_pointer<intrinsic_t<T>>::type,
|
||||
conditional_t<std::is_rvalue_reference<T>::value,
|
||||
typename std::add_rvalue_reference<intrinsic_t<T>>::type,
|
||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>>;
|
||||
|
||||
// std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
|
||||
// T is non-copyable, but code containing such a copy constructor fails to actually compile.
|
||||
template <typename T, typename SFINAE = void> struct is_copy_constructible : std::is_copy_constructible<T> {};
|
||||
|
||||
// Specialization for types that appear to be copy constructible but also look like stl containers
|
||||
// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
|
||||
// so, copy constructability depends on whether the value_type is copy constructible.
|
||||
template <typename Container> struct is_copy_constructible<Container, enable_if_t<all_of<
|
||||
std::is_copy_constructible<Container>,
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>,
|
||||
// Avoid infinite recursion
|
||||
negation<std::is_same<Container, typename Container::value_type>>
|
||||
>::value>> : is_copy_constructible<typename Container::value_type> {};
|
||||
|
||||
// Likewise for std::pair
|
||||
// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves
|
||||
// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers).
|
||||
template <typename T1, typename T2> struct is_copy_constructible<std::pair<T1, T2>>
|
||||
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
|
||||
|
||||
// The same problems arise with std::is_copy_assignable, so we use the same workaround.
|
||||
template <typename T, typename SFINAE = void> struct is_copy_assignable : std::is_copy_assignable<T> {};
|
||||
template <typename Container> struct is_copy_assignable<Container, enable_if_t<all_of<
|
||||
std::is_copy_assignable<Container>,
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>
|
||||
>::value>> : is_copy_assignable<typename Container::value_type> {};
|
||||
template <typename T1, typename T2> struct is_copy_assignable<std::pair<T1, T2>>
|
||||
: all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
// polymorphic_type_hook<itype>::get(src, tinfo) determines whether the object pointed
|
||||
// to by `src` actually is an instance of some class derived from `itype`.
|
||||
// If so, it sets `tinfo` to point to the std::type_info representing that derived
|
||||
// type, and returns a pointer to the start of the most-derived object of that type
|
||||
// (in which `src` is a subobject; this will be the same address as `src` in most
|
||||
// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src`
|
||||
// and leaves `tinfo` at its default value of nullptr.
|
||||
//
|
||||
// The default polymorphic_type_hook just returns src. A specialization for polymorphic
|
||||
// types determines the runtime type of the passed object and adjusts the this-pointer
|
||||
// appropriately via dynamic_cast<void*>. This is what enables a C++ Animal* to appear
|
||||
// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is
|
||||
// registered with pybind11, and this Animal is in fact a Dog).
|
||||
//
|
||||
// You may specialize polymorphic_type_hook yourself for types that want to appear
|
||||
// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern
|
||||
// in performance-sensitive applications, used most notably in LLVM.)
|
||||
//
|
||||
// polymorphic_type_hook_base allows users to specialize polymorphic_type_hook with
|
||||
// std::enable_if. User provided specializations will always have higher priority than
|
||||
// the default implementation and specialization provided in polymorphic_type_hook_base.
|
||||
template <typename itype, typename SFINAE = void>
|
||||
struct polymorphic_type_hook_base
|
||||
{
|
||||
static const void *get(const itype *src, const std::type_info*&) { return src; }
|
||||
};
|
||||
template <typename itype>
|
||||
struct polymorphic_type_hook_base<itype, detail::enable_if_t<std::is_polymorphic<itype>::value>>
|
||||
{
|
||||
static const void *get(const itype *src, const std::type_info*& type) {
|
||||
type = src ? &typeid(*src) : nullptr;
|
||||
return dynamic_cast<const void*>(src);
|
||||
}
|
||||
};
|
||||
template <typename itype, typename SFINAE = void>
|
||||
struct polymorphic_type_hook : public polymorphic_type_hook_base<itype> {};
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Generic type caster for objects stored on the heap
|
||||
template <typename type> class type_caster_base : public type_caster_generic {
|
||||
using itype = intrinsic_t<type>;
|
||||
|
||||
public:
|
||||
static constexpr auto name = _<type>();
|
||||
|
||||
type_caster_base() : type_caster_base(typeid(type)) { }
|
||||
explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { }
|
||||
|
||||
static handle cast(const itype &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference)
|
||||
policy = return_value_policy::copy;
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(itype &&src, return_value_policy, handle parent) {
|
||||
return cast(&src, return_value_policy::move, parent);
|
||||
}
|
||||
|
||||
// Returns a (pointer, type_info) pair taking care of necessary type lookup for a
|
||||
// polymorphic type (using RTTI by default, but can be overridden by specializing
|
||||
// polymorphic_type_hook). If the instance isn't derived, returns the base version.
|
||||
static std::pair<const void *, const type_info *> src_and_type(const itype *src) {
|
||||
auto &cast_type = typeid(itype);
|
||||
const std::type_info *instance_type = nullptr;
|
||||
const void *vsrc = polymorphic_type_hook<itype>::get(src, instance_type);
|
||||
if (instance_type && !same_type(cast_type, *instance_type)) {
|
||||
// This is a base pointer to a derived type. If the derived type is registered
|
||||
// with pybind11, we want to make the full derived object available.
|
||||
// In the typical case where itype is polymorphic, we get the correct
|
||||
// derived pointer (which may be != base pointer) by a dynamic_cast to
|
||||
// most derived type. If itype is not polymorphic, we won't get here
|
||||
// except via a user-provided specialization of polymorphic_type_hook,
|
||||
// and the user has promised that no this-pointer adjustment is
|
||||
// required in that case, so it's OK to use static_cast.
|
||||
if (const auto *tpi = get_type_info(*instance_type))
|
||||
return {vsrc, tpi};
|
||||
}
|
||||
// Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so
|
||||
// don't do a cast
|
||||
return type_caster_generic::src_and_type(src, cast_type, instance_type);
|
||||
}
|
||||
|
||||
static handle cast(const itype *src, return_value_policy policy, handle parent) {
|
||||
auto st = src_and_type(src);
|
||||
return type_caster_generic::cast(
|
||||
st.first, policy, parent, st.second,
|
||||
make_copy_constructor(src), make_move_constructor(src));
|
||||
}
|
||||
|
||||
static handle cast_holder(const itype *src, const void *holder) {
|
||||
auto st = src_and_type(src);
|
||||
return type_caster_generic::cast(
|
||||
st.first, return_value_policy::take_ownership, {}, st.second,
|
||||
nullptr, nullptr, holder);
|
||||
}
|
||||
|
||||
template <typename T> using cast_op_type = detail::cast_op_type<T>;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator itype*() { return (type *) value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); }
|
||||
|
||||
protected:
|
||||
using Constructor = void *(*)(const void *);
|
||||
|
||||
/* Only enabled when the types are {copy,move}-constructible *and* when the type
|
||||
does not have a private operator new implementation. A comma operator is used in the decltype
|
||||
argument to apply SFINAE to the public copy/move constructors.*/
|
||||
template <typename T, typename = enable_if_t<is_copy_constructible<T>::value>>
|
||||
static auto make_copy_constructor(const T *) -> decltype(new T(std::declval<const T>()), Constructor{}) {
|
||||
return [](const void *arg) -> void * {
|
||||
return new T(*reinterpret_cast<const T *>(arg));
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T, typename = enable_if_t<std::is_move_constructible<T>::value>>
|
||||
static auto make_move_constructor(const T *) -> decltype(new T(std::declval<T&&>()), Constructor{}) {
|
||||
return [](const void *arg) -> void * {
|
||||
return new T(std::move(*const_cast<T *>(reinterpret_cast<const T *>(arg))));
|
||||
};
|
||||
}
|
||||
|
||||
static Constructor make_copy_constructor(...) { return nullptr; }
|
||||
static Constructor make_move_constructor(...) { return nullptr; }
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
@@ -29,7 +29,7 @@ inline void erase_all(std::string &string, const std::string &search) {
|
||||
}
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE inline void clean_type_id(std::string &name) {
|
||||
PYBIND11_NOINLINE void clean_type_id(std::string &name) {
|
||||
#if defined(__GNUG__)
|
||||
int status = 0;
|
||||
std::unique_ptr<char, void (*)(void *)> res {
|
||||
|
||||
@@ -9,33 +9,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
|
||||
See also:
|
||||
https://stackoverflow.com/questions/2579576/i-dir-vs-isystem-dir
|
||||
https://stackoverflow.com/questions/1741816/isystem-for-ms-visual-studio-c-compiler
|
||||
*/
|
||||
|
||||
#include "numpy.h"
|
||||
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
|
||||
#elif defined(__GNUG__) || defined(__clang__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wconversion"
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
# ifdef __clang__
|
||||
// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated
|
||||
// under Clang, so disable that warning here:
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
# endif
|
||||
# if __GNUC__ >= 7
|
||||
# pragma GCC diagnostic ignored "-Wint-in-bool-context"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// The C4127 suppression was introduced for Eigen 3.4.0. In theory we could
|
||||
// make it version specific, or even remove it later, but considering that
|
||||
// 1. C4127 is generally far more distracting than useful for modern template code, and
|
||||
// 2. we definitely want to ignore any MSVC warnings originating from Eigen code,
|
||||
// it is probably best to keep this around indefinitely.
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17
|
||||
# pragma warning(disable: 4127) // C4127: conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#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.
|
||||
@@ -77,6 +75,7 @@ template <bool EigenRowMajor> struct EigenConformable {
|
||||
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
|
||||
bool negativestrides = false; // If true, do not use stride!
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
EigenConformable(bool fits = false) : conformable{fits} {}
|
||||
// Matrix type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c,
|
||||
@@ -104,6 +103,7 @@ template <bool EigenRowMajor> struct EigenConformable {
|
||||
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() ||
|
||||
(EigenRowMajor ? rows : cols) == 1);
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator bool() const { return conformable; }
|
||||
};
|
||||
|
||||
@@ -153,7 +153,8 @@ template <typename Type_> struct EigenProps {
|
||||
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))
|
||||
if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows) ||
|
||||
(PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols))
|
||||
return false;
|
||||
|
||||
return {np_rows, np_cols, np_rstride, np_cstride};
|
||||
@@ -165,25 +166,22 @@ template <typename Type_> struct EigenProps {
|
||||
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
|
||||
if (vector) { // Eigen type is a compile-time vector
|
||||
if (fixed && size != n)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n)
|
||||
return false; // Vector size mismatch
|
||||
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
|
||||
}
|
||||
else if (fixed) {
|
||||
if (fixed) {
|
||||
// The type has a fixed size, but is not a vector: abort
|
||||
return false;
|
||||
}
|
||||
else if (fixed_cols) {
|
||||
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;
|
||||
} // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) return false;
|
||||
return {n, 1, stride};
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
@@ -344,8 +342,11 @@ public:
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type*() { return &value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&() { return value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&&() && { return std::move(value); }
|
||||
template <typename T> using cast_op_type = movable_cast_op_type<T>;
|
||||
|
||||
@@ -469,7 +470,9 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type*() { return ref.get(); }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&() { return *ref; }
|
||||
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
|
||||
|
||||
@@ -570,7 +573,9 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
if (!values || !innerIndices || !outerIndices)
|
||||
return false;
|
||||
|
||||
value = Eigen::MappedSparseMatrix<Scalar, Type::Flags, StorageIndex>(
|
||||
value = Eigen::MappedSparseMatrix<Scalar,
|
||||
Type::Flags & (Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(
|
||||
shape[0].cast<Index>(), shape[1].cast<Index>(), nnz,
|
||||
outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data());
|
||||
|
||||
@@ -599,9 +604,3 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(__GNUG__) || defined(__clang__)
|
||||
# pragma GCC diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#include "pybind11.h"
|
||||
#include "eval.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#if defined(PYPY_VERSION)
|
||||
# error Embedding the interpreter is not supported with PyPy
|
||||
#endif
|
||||
@@ -45,25 +48,23 @@
|
||||
});
|
||||
}
|
||||
\endrst */
|
||||
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||
static ::pybind11::module_::module_def \
|
||||
PYBIND11_CONCAT(pybind11_module_def_, name); \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
PYBIND11_TOSTRING(name), nullptr, \
|
||||
&PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \
|
||||
(PYBIND11_TOSTRING(name), \
|
||||
PYBIND11_CONCAT(pybind11_init_impl_, name)); \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
|
||||
|
||||
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
|
||||
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
|
||||
auto m = ::pybind11::module_::create_extension_module( \
|
||||
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||
try { \
|
||||
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||
return m.ptr(); \
|
||||
} \
|
||||
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
} \
|
||||
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \
|
||||
PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \
|
||||
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \
|
||||
& variable) // NOLINT(bugprone-macro-parentheses)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
@@ -76,7 +77,7 @@ struct embedded_module {
|
||||
using init_t = void (*)();
|
||||
#endif
|
||||
embedded_module(const char *name, init_t init) {
|
||||
if (Py_IsInitialized())
|
||||
if (Py_IsInitialized() != 0)
|
||||
pybind11_fail("Can't add new modules after the interpreter has been initialized");
|
||||
|
||||
auto result = PyImport_AppendInittab(name, init);
|
||||
@@ -85,29 +86,106 @@ struct embedded_module {
|
||||
}
|
||||
};
|
||||
|
||||
struct wide_char_arg_deleter {
|
||||
void operator()(wchar_t *ptr) const {
|
||||
#if PY_VERSION_HEX >= 0x030500f0
|
||||
// API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
|
||||
PyMem_RawFree(ptr);
|
||||
#else
|
||||
delete[] ptr;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
inline wchar_t *widen_chars(const char *safe_arg) {
|
||||
#if PY_VERSION_HEX >= 0x030500f0
|
||||
wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr);
|
||||
#else
|
||||
wchar_t *widened_arg = nullptr;
|
||||
# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS
|
||||
size_t count = strlen(safe_arg);
|
||||
# else
|
||||
size_t count = mbstowcs(nullptr, safe_arg, 0);
|
||||
# endif
|
||||
if (count != static_cast<size_t>(-1)) {
|
||||
widened_arg = new wchar_t[count + 1];
|
||||
mbstowcs(widened_arg, safe_arg, count + 1);
|
||||
}
|
||||
#endif
|
||||
return widened_arg;
|
||||
}
|
||||
|
||||
/// Python 2.x/3.x-compatible version of `PySys_SetArgv`
|
||||
inline void set_interpreter_argv(int argc, const char *const *argv, bool add_program_dir_to_path) {
|
||||
// Before it was special-cased in python 3.8, passing an empty or null argv
|
||||
// caused a segfault, so we have to reimplement the special case ourselves.
|
||||
bool special_case = (argv == nullptr || argc <= 0);
|
||||
|
||||
const char *const empty_argv[]{"\0"};
|
||||
const char *const *safe_argv = special_case ? empty_argv : argv;
|
||||
if (special_case)
|
||||
argc = 1;
|
||||
|
||||
auto argv_size = static_cast<size_t>(argc);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// SetArgv* on python 3 takes wchar_t, so we have to convert.
|
||||
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
|
||||
std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> widened_argv_entries;
|
||||
widened_argv_entries.reserve(argv_size);
|
||||
for (size_t ii = 0; ii < argv_size; ++ii) {
|
||||
widened_argv_entries.emplace_back(widen_chars(safe_argv[ii]));
|
||||
if (!widened_argv_entries.back()) {
|
||||
// A null here indicates a character-encoding failure or the python
|
||||
// interpreter out of memory. Give up.
|
||||
return;
|
||||
}
|
||||
widened_argv[ii] = widened_argv_entries.back().get();
|
||||
}
|
||||
|
||||
auto pysys_argv = widened_argv.get();
|
||||
#else
|
||||
// python 2.x
|
||||
std::vector<std::string> strings{safe_argv, safe_argv + argv_size};
|
||||
std::vector<char *> char_strings{argv_size};
|
||||
for (std::size_t i = 0; i < argv_size; ++i)
|
||||
char_strings[i] = &strings[i][0];
|
||||
char **pysys_argv = char_strings.data();
|
||||
#endif
|
||||
|
||||
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
|
||||
}
|
||||
|
||||
PYBIND11_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.
|
||||
optional `init_signal_handlers` 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.
|
||||
|
||||
If initializing the Python interpreter fails, then the program is terminated. (This
|
||||
is controlled by the CPython runtime and is an exception to pybind11's normal behavior
|
||||
of throwing exceptions on errors.)
|
||||
|
||||
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
|
||||
used to populate ``sys.argv`` and ``sys.path``.
|
||||
See the |PySys_SetArgvEx documentation|_ for details.
|
||||
|
||||
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
|
||||
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
|
||||
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
|
||||
\endrst */
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true) {
|
||||
if (Py_IsInitialized())
|
||||
inline void initialize_interpreter(bool init_signal_handlers = true,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
if (Py_IsInitialized() != 0)
|
||||
pybind11_fail("The interpreter is already running");
|
||||
|
||||
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||
|
||||
// Make .py files in the working directory available by default
|
||||
module_::import("sys").attr("path").cast<list>().append(".");
|
||||
detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
|
||||
/** \rst
|
||||
@@ -169,6 +247,8 @@ inline void finalize_interpreter() {
|
||||
Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
|
||||
This a move-only guard and only a single instance can exist.
|
||||
|
||||
See `initialize_interpreter` for a discussion of its constructor arguments.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <pybind11/embed.h>
|
||||
@@ -180,8 +260,11 @@ inline void finalize_interpreter() {
|
||||
\endrst */
|
||||
class scoped_interpreter {
|
||||
public:
|
||||
scoped_interpreter(bool init_signal_handlers = true) {
|
||||
initialize_interpreter(init_signal_handlers);
|
||||
explicit scoped_interpreter(bool init_signal_handlers = true,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
|
||||
scoped_interpreter(const scoped_interpreter &) = delete;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
pybind11/exec.h: Support for evaluating Python expressions and statements
|
||||
pybind11/eval.h: Support for evaluating Python expressions and statements
|
||||
from strings and files
|
||||
|
||||
Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
@@ -43,7 +45,7 @@ enum eval_mode {
|
||||
};
|
||||
|
||||
template <eval_mode mode = eval_expr>
|
||||
object eval(str expr, object global = globals(), object local = object()) {
|
||||
object eval(const str &expr, object global = globals(), object local = object()) {
|
||||
if (!local)
|
||||
local = global;
|
||||
|
||||
@@ -53,7 +55,7 @@ object eval(str expr, object global = globals(), object local = object()) {
|
||||
this seems to be the only alternative */
|
||||
std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr;
|
||||
|
||||
int start;
|
||||
int start = 0;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_input; break;
|
||||
case eval_single_statement: start = Py_single_input; break;
|
||||
@@ -75,8 +77,8 @@ object eval(const char (&s)[N], object global = globals(), object local = object
|
||||
return eval<mode>(expr, global, local);
|
||||
}
|
||||
|
||||
inline void exec(str expr, object global = globals(), object local = object()) {
|
||||
eval<eval_statements>(expr, global, local);
|
||||
inline void exec(const str &expr, object global = globals(), object local = object()) {
|
||||
eval<eval_statements>(expr, std::move(global), std::move(local));
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
@@ -105,7 +107,7 @@ object eval_file(str fname, object global = globals(), object local = object())
|
||||
|
||||
detail::ensure_builtins_in_globals(global);
|
||||
|
||||
int start;
|
||||
int start = 0;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_input; break;
|
||||
case eval_single_statement: start = Py_single_input; break;
|
||||
@@ -134,6 +136,15 @@ object eval_file(str fname, object global = globals(), object local = object())
|
||||
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
|
||||
}
|
||||
|
||||
// In Python2, this should be encoded by getfilesystemencoding.
|
||||
// We don't boher setting it since Python2 is past EOL anyway.
|
||||
// See PR#3233
|
||||
#if PY_VERSION_HEX >= 0x03000000
|
||||
if (!global.contains("__file__")) {
|
||||
global["__file__"] = std::move(fname);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
|
||||
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
|
||||
local.ptr());
|
||||
|
||||
@@ -43,24 +43,42 @@ public:
|
||||
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;
|
||||
auto cfunc_self = PyCFunction_GET_SELF(cfunc.ptr());
|
||||
if (isinstance<capsule>(cfunc_self)) {
|
||||
auto c = reinterpret_borrow<capsule>(cfunc_self);
|
||||
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;
|
||||
while (rec != nullptr) {
|
||||
if (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;
|
||||
}
|
||||
rec = rec->next;
|
||||
}
|
||||
}
|
||||
// PYPY segfaults here when passing builtin function like sum.
|
||||
// Raising an fail exception here works to prevent the segfault, but only on gcc.
|
||||
// See PR #1413 for full details
|
||||
}
|
||||
|
||||
// ensure GIL is held during functor destruction
|
||||
struct func_handle {
|
||||
function f;
|
||||
func_handle(function&& f_) : f(std::move(f_)) {}
|
||||
func_handle(const func_handle& f_) {
|
||||
#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17))
|
||||
// This triggers a syntax error under very special conditions (very weird indeed).
|
||||
explicit
|
||||
#endif
|
||||
func_handle(function &&f_) noexcept : f(std::move(f_)) {}
|
||||
func_handle(const func_handle &f_) { operator=(f_); }
|
||||
func_handle &operator=(const func_handle &f_) {
|
||||
gil_scoped_acquire acq;
|
||||
f = f_.f;
|
||||
return *this;
|
||||
}
|
||||
~func_handle() {
|
||||
gil_scoped_acquire acq;
|
||||
@@ -71,7 +89,7 @@ public:
|
||||
// to emulate 'move initialization capture' in C++11
|
||||
struct func_wrapper {
|
||||
func_handle hfunc;
|
||||
func_wrapper(func_handle&& hf): hfunc(std::move(hf)) {}
|
||||
explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
|
||||
Return operator()(Args... args) const {
|
||||
gil_scoped_acquire acq;
|
||||
object retval(hfunc.f(std::forward<Args>(args)...));
|
||||
@@ -92,8 +110,7 @@ public:
|
||||
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();
|
||||
return cpp_function(std::forward<Func>(f_), policy).release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster<Args>::name...) + _("], ")
|
||||
|
||||
193
ext/pybind11/include/pybind11/gil.h
Normal file
193
ext/pybind11/include/pybind11/gil.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
pybind11/gil.h: RAII helpers for managing the GIL
|
||||
|
||||
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 "detail/common.h"
|
||||
#include "detail/internals.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// forward declarations
|
||||
PyThreadState *get_thread_state_unchecked();
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
|
||||
|
||||
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
||||
* pattern, but there are a few important differences:
|
||||
*
|
||||
* 1. When acquiring the GIL from an non-main thread during the finalization
|
||||
* phase, the GILState API blindly terminates the calling thread, which
|
||||
* is often not what is wanted. This API does not do this.
|
||||
*
|
||||
* 2. The gil_scoped_release function can optionally cut the relationship
|
||||
* of a PyThreadState and its associated thread, which allows moving it to
|
||||
* another thread (this is a fairly rare/advanced use case).
|
||||
*
|
||||
* 3. The reference count of an acquired thread state can be controlled. This
|
||||
* can be handy to prevent cases where callbacks issued from an external
|
||||
* thread would otherwise constantly construct and destroy thread state data
|
||||
* structures.
|
||||
*
|
||||
* See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an
|
||||
* example which uses features 2 and 3 to migrate the Python thread of
|
||||
* execution to another thread (to run the event loop on the original thread,
|
||||
* in this case).
|
||||
*/
|
||||
|
||||
class gil_scoped_acquire {
|
||||
public:
|
||||
PYBIND11_NOINLINE gil_scoped_acquire() {
|
||||
auto &internals = detail::get_internals();
|
||||
tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
|
||||
|
||||
if (!tstate) {
|
||||
/* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if
|
||||
calling from a Python thread). Since we use a different key, this ensures
|
||||
we don't create a new thread state and deadlock in PyEval_AcquireThread
|
||||
below. Note we don't save this state with internals.tstate, since we don't
|
||||
create it we would fail to clear it (its reference count should be > 0). */
|
||||
tstate = PyGILState_GetThisThreadState();
|
||||
}
|
||||
|
||||
if (!tstate) {
|
||||
tstate = PyThreadState_New(internals.istate);
|
||||
#if !defined(NDEBUG)
|
||||
if (!tstate)
|
||||
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||
#endif
|
||||
tstate->gilstate_counter = 0;
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
|
||||
} else {
|
||||
release = detail::get_thread_state_unchecked() != tstate;
|
||||
}
|
||||
|
||||
if (release) {
|
||||
PyEval_AcquireThread(tstate);
|
||||
}
|
||||
|
||||
inc_ref();
|
||||
}
|
||||
|
||||
void inc_ref() {
|
||||
++tstate->gilstate_counter;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE void dec_ref() {
|
||||
--tstate->gilstate_counter;
|
||||
#if !defined(NDEBUG)
|
||||
if (detail::get_thread_state_unchecked() != tstate)
|
||||
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
|
||||
if (tstate->gilstate_counter < 0)
|
||||
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
|
||||
#endif
|
||||
if (tstate->gilstate_counter == 0) {
|
||||
#if !defined(NDEBUG)
|
||||
if (!release)
|
||||
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||
#endif
|
||||
PyThreadState_Clear(tstate);
|
||||
if (active)
|
||||
PyThreadState_DeleteCurrent();
|
||||
PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate);
|
||||
release = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// This method will disable the PyThreadState_DeleteCurrent call and the
|
||||
/// GIL won't be acquired. This method should be used if the interpreter
|
||||
/// could be shutting down when this is called, as thread deletion is not
|
||||
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||
/// protect subsequent code.
|
||||
PYBIND11_NOINLINE void disarm() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE ~gil_scoped_acquire() {
|
||||
dec_ref();
|
||||
if (release)
|
||||
PyEval_SaveThread();
|
||||
}
|
||||
private:
|
||||
PyThreadState *tstate = nullptr;
|
||||
bool release = true;
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
class gil_scoped_release {
|
||||
public:
|
||||
explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) {
|
||||
// `get_internals()` must be called here unconditionally in order to initialize
|
||||
// `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
|
||||
// initialization race could occur as multiple threads try `gil_scoped_acquire`.
|
||||
auto &internals = detail::get_internals();
|
||||
tstate = PyEval_SaveThread();
|
||||
if (disassoc) {
|
||||
auto key = internals.tstate;
|
||||
PYBIND11_TLS_DELETE_VALUE(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// This method will disable the PyThreadState_DeleteCurrent call and the
|
||||
/// GIL won't be acquired. This method should be used if the interpreter
|
||||
/// could be shutting down when this is called, as thread deletion is not
|
||||
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||
/// protect subsequent code.
|
||||
PYBIND11_NOINLINE void disarm() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
~gil_scoped_release() {
|
||||
if (!tstate)
|
||||
return;
|
||||
// `PyEval_RestoreThread()` should not be called if runtime is finalizing
|
||||
if (active)
|
||||
PyEval_RestoreThread(tstate);
|
||||
if (disassoc) {
|
||||
auto key = detail::get_internals().tstate;
|
||||
PYBIND11_TLS_REPLACE_VALUE(key, tstate);
|
||||
}
|
||||
}
|
||||
private:
|
||||
PyThreadState *tstate;
|
||||
bool disassoc;
|
||||
bool active = true;
|
||||
};
|
||||
#elif defined(PYPY_VERSION)
|
||||
class gil_scoped_acquire {
|
||||
PyGILState_STATE state;
|
||||
public:
|
||||
gil_scoped_acquire() { state = PyGILState_Ensure(); }
|
||||
~gil_scoped_acquire() { PyGILState_Release(state); }
|
||||
void disarm() {}
|
||||
};
|
||||
|
||||
class gil_scoped_release {
|
||||
PyThreadState *state;
|
||||
public:
|
||||
gil_scoped_release() { state = PyEval_SaveThread(); }
|
||||
~gil_scoped_release() { PyEval_RestoreThread(state); }
|
||||
void disarm() {}
|
||||
};
|
||||
#else
|
||||
class gil_scoped_acquire {
|
||||
void disarm() {}
|
||||
};
|
||||
class gil_scoped_release {
|
||||
void disarm() {}
|
||||
};
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
@@ -5,17 +5,31 @@
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
WARNING: The implementation in this file is NOT thread safe. Multiple
|
||||
threads writing to a redirected ostream concurrently cause data races
|
||||
and potentially buffer overflows. Therefore it is currently a requirement
|
||||
that all (possibly) concurrent redirected ostream writes are protected by
|
||||
a mutex.
|
||||
#HelpAppreciated: Work on iostream.h thread safety.
|
||||
For more background see the discussions under
|
||||
https://github.com/pybind/pybind11/pull/2982 and
|
||||
https://github.com/pybind/pybind11/pull/2995.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <streambuf>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
@@ -38,25 +52,68 @@ private:
|
||||
return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof();
|
||||
}
|
||||
|
||||
// This function must be non-virtual to be called in a destructor. If the
|
||||
// rare MSVC test failure shows up with this version, then this should be
|
||||
// simplified to a fully qualified call.
|
||||
// Computes how many bytes at the end of the buffer are part of an
|
||||
// incomplete sequence of UTF-8 bytes.
|
||||
// Precondition: pbase() < pptr()
|
||||
size_t utf8_remainder() const {
|
||||
const auto rbase = std::reverse_iterator<char *>(pbase());
|
||||
const auto rpptr = std::reverse_iterator<char *>(pptr());
|
||||
auto is_ascii = [](char c) {
|
||||
return (static_cast<unsigned char>(c) & 0x80) == 0x00;
|
||||
};
|
||||
auto is_leading = [](char c) {
|
||||
return (static_cast<unsigned char>(c) & 0xC0) == 0xC0;
|
||||
};
|
||||
auto is_leading_2b = [](char c) {
|
||||
return static_cast<unsigned char>(c) <= 0xDF;
|
||||
};
|
||||
auto is_leading_3b = [](char c) {
|
||||
return static_cast<unsigned char>(c) <= 0xEF;
|
||||
};
|
||||
// If the last character is ASCII, there are no incomplete code points
|
||||
if (is_ascii(*rpptr))
|
||||
return 0;
|
||||
// Otherwise, work back from the end of the buffer and find the first
|
||||
// UTF-8 leading byte
|
||||
const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase;
|
||||
const auto leading = std::find_if(rpptr, rpend, is_leading);
|
||||
if (leading == rbase)
|
||||
return 0;
|
||||
const auto dist = static_cast<size_t>(leading - rpptr);
|
||||
size_t remainder = 0;
|
||||
|
||||
if (dist == 0)
|
||||
remainder = 1; // 1-byte code point is impossible
|
||||
else if (dist == 1)
|
||||
remainder = is_leading_2b(*leading) ? 0 : dist + 1;
|
||||
else if (dist == 2)
|
||||
remainder = is_leading_3b(*leading) ? 0 : dist + 1;
|
||||
// else if (dist >= 3), at least 4 bytes before encountering an UTF-8
|
||||
// leading byte, either no remainder or invalid UTF-8.
|
||||
// Invalid UTF-8 will cause an exception later when converting
|
||||
// to a Python string, so that's not handled here.
|
||||
return remainder;
|
||||
}
|
||||
|
||||
// This function must be non-virtual to be called in a destructor.
|
||||
int _sync() {
|
||||
if (pbase() != pptr()) {
|
||||
|
||||
{
|
||||
gil_scoped_acquire tmp;
|
||||
|
||||
// This subtraction cannot be negative, so dropping the sign.
|
||||
str line(pbase(), static_cast<size_t>(pptr() - pbase()));
|
||||
if (pbase() != pptr()) { // If buffer is not empty
|
||||
gil_scoped_acquire tmp;
|
||||
// This subtraction cannot be negative, so dropping the sign.
|
||||
auto size = static_cast<size_t>(pptr() - pbase());
|
||||
size_t remainder = utf8_remainder();
|
||||
|
||||
if (size > remainder) {
|
||||
str line(pbase(), size - remainder);
|
||||
pywrite(line);
|
||||
pyflush();
|
||||
|
||||
// Placed inside gil_scoped_aquire as a mutex to avoid a race
|
||||
setp(pbase(), epptr());
|
||||
}
|
||||
|
||||
// Copy the remainder at the end of the buffer to the beginning:
|
||||
if (remainder > 0)
|
||||
std::memmove(pbase(), pptr() - remainder, remainder);
|
||||
setp(pbase(), epptr());
|
||||
pbump(static_cast<int>(remainder));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -66,11 +123,8 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
pythonbuf(object pyostream, size_t buffer_size = 1024)
|
||||
: buf_size(buffer_size),
|
||||
d_buffer(new char[buf_size]),
|
||||
pywrite(pyostream.attr("write")),
|
||||
explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024)
|
||||
: buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")),
|
||||
pyflush(pyostream.attr("flush")) {
|
||||
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
|
||||
}
|
||||
@@ -117,9 +171,9 @@ protected:
|
||||
detail::pythonbuf buffer;
|
||||
|
||||
public:
|
||||
scoped_ostream_redirect(
|
||||
std::ostream &costream = std::cout,
|
||||
object pyostream = module_::import("sys").attr("stdout"))
|
||||
explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
|
||||
const object &pyostream
|
||||
= module_::import("sys").attr("stdout"))
|
||||
: costream(costream), buffer(pyostream) {
|
||||
old = costream.rdbuf(&buffer);
|
||||
}
|
||||
@@ -148,10 +202,10 @@ public:
|
||||
\endrst */
|
||||
class scoped_estream_redirect : public scoped_ostream_redirect {
|
||||
public:
|
||||
scoped_estream_redirect(
|
||||
std::ostream &costream = std::cerr,
|
||||
object pyostream = module_::import("sys").attr("stderr"))
|
||||
: scoped_ostream_redirect(costream,pyostream) {}
|
||||
explicit scoped_estream_redirect(std::ostream &costream = std::cerr,
|
||||
const object &pyostream
|
||||
= module_::import("sys").attr("stderr"))
|
||||
: scoped_ostream_redirect(costream, pyostream) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -165,7 +219,7 @@ class OstreamRedirect {
|
||||
std::unique_ptr<scoped_estream_redirect> redirect_stderr;
|
||||
|
||||
public:
|
||||
OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
|
||||
explicit OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
|
||||
: do_stdout_(do_stdout), do_stderr_(do_stderr) {}
|
||||
|
||||
void enter() {
|
||||
@@ -210,11 +264,12 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
m.noisy_function_with_error_printing()
|
||||
|
||||
\endrst */
|
||||
inline class_<detail::OstreamRedirect> add_ostream_redirect(module_ m, std::string name = "ostream_redirect") {
|
||||
return class_<detail::OstreamRedirect>(m, name.c_str(), module_local())
|
||||
.def(init<bool,bool>(), arg("stdout")=true, arg("stderr")=true)
|
||||
inline class_<detail::OstreamRedirect>
|
||||
add_ostream_redirect(module_ m, const std::string &name = "ostream_redirect") {
|
||||
return class_<detail::OstreamRedirect>(std::move(m), name.c_str(), module_local())
|
||||
.def(init<bool, bool>(), arg("stdout") = true, arg("stderr") = true)
|
||||
.def("__enter__", &detail::OstreamRedirect::enter)
|
||||
.def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); });
|
||||
.def("__exit__", [](detail::OstreamRedirect &self_, const args &) { self_.exit(); });
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
@@ -25,11 +25,6 @@
|
||||
#include <vector>
|
||||
#include <typeindex>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
#endif
|
||||
|
||||
/* This will be true on all flat address space platforms and allows us to reduce the
|
||||
whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size
|
||||
and dimension types (e.g. shape, strides, indexing), instead of inflicting this
|
||||
@@ -104,7 +99,7 @@ struct numpy_internals {
|
||||
}
|
||||
};
|
||||
|
||||
inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) {
|
||||
PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) {
|
||||
ptr = &get_or_create_shared_data<numpy_internals>("_numpy_internals");
|
||||
}
|
||||
|
||||
@@ -164,10 +159,10 @@ struct npy_api {
|
||||
NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct PyArray_Dims {
|
||||
Py_intptr_t *ptr;
|
||||
int len;
|
||||
} PyArray_Dims;
|
||||
};
|
||||
|
||||
static npy_api& get() {
|
||||
static npy_api api = lookup();
|
||||
@@ -203,6 +198,9 @@ struct npy_api {
|
||||
// Unused. Not removed because that affects ABI of the class.
|
||||
int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
|
||||
PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int);
|
||||
PyObject* (*PyArray_Newshape_)(PyObject*, PyArray_Dims*, int);
|
||||
PyObject* (*PyArray_View_)(PyObject*, PyObject*, PyObject*);
|
||||
|
||||
private:
|
||||
enum functions {
|
||||
API_PyArray_GetNDArrayCFeatureVersion = 211,
|
||||
@@ -217,10 +215,12 @@ private:
|
||||
API_PyArray_NewCopy = 85,
|
||||
API_PyArray_NewFromDescr = 94,
|
||||
API_PyArray_DescrNewFromType = 96,
|
||||
API_PyArray_Newshape = 135,
|
||||
API_PyArray_Squeeze = 136,
|
||||
API_PyArray_View = 137,
|
||||
API_PyArray_DescrConverter = 174,
|
||||
API_PyArray_EquivTypes = 182,
|
||||
API_PyArray_GetArrayParamsFromObject = 278,
|
||||
API_PyArray_Squeeze = 136,
|
||||
API_PyArray_SetBaseObject = 282
|
||||
};
|
||||
|
||||
@@ -248,11 +248,14 @@ private:
|
||||
DECL_NPY_API(PyArray_NewCopy);
|
||||
DECL_NPY_API(PyArray_NewFromDescr);
|
||||
DECL_NPY_API(PyArray_DescrNewFromType);
|
||||
DECL_NPY_API(PyArray_Newshape);
|
||||
DECL_NPY_API(PyArray_Squeeze);
|
||||
DECL_NPY_API(PyArray_View);
|
||||
DECL_NPY_API(PyArray_DescrConverter);
|
||||
DECL_NPY_API(PyArray_EquivTypes);
|
||||
DECL_NPY_API(PyArray_GetArrayParamsFromObject);
|
||||
DECL_NPY_API(PyArray_Squeeze);
|
||||
DECL_NPY_API(PyArray_SetBaseObject);
|
||||
|
||||
#undef DECL_NPY_API
|
||||
return api;
|
||||
}
|
||||
@@ -319,14 +322,13 @@ template <typename T> using remove_all_extents_t = typename array_info<T>::type;
|
||||
|
||||
template <typename T> using is_pod_struct = all_of<
|
||||
std::is_standard_layout<T>, // since we're accessing directly in memory we need a standard layout type
|
||||
#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI)
|
||||
// _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent
|
||||
// of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4).
|
||||
std::is_trivially_copyable<T>,
|
||||
#else
|
||||
// GCC 4 doesn't implement is_trivially_copyable, so approximate it
|
||||
#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150426 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
|
||||
// libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after 5)
|
||||
// don't implement is_trivially_copyable, so approximate it
|
||||
std::is_trivially_destructible<T>,
|
||||
satisfies_any_of<T, std::has_trivial_copy_constructor, std::has_trivial_copy_assign>,
|
||||
#else
|
||||
std::is_trivially_copyable<T>,
|
||||
#endif
|
||||
satisfies_none_of<T, std::is_reference, std::is_array, is_std_array, std::is_arithmetic, is_complex, std::is_enum>
|
||||
>;
|
||||
@@ -466,28 +468,30 @@ public:
|
||||
explicit dtype(const buffer_info &info) {
|
||||
dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format)));
|
||||
// If info.itemsize == 0, use the value calculated from the format string
|
||||
m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr();
|
||||
m_ptr = descr.strip_padding(info.itemsize != 0 ? info.itemsize : descr.itemsize())
|
||||
.release()
|
||||
.ptr();
|
||||
}
|
||||
|
||||
explicit dtype(const std::string &format) {
|
||||
m_ptr = from_args(pybind11::str(format)).release().ptr();
|
||||
}
|
||||
|
||||
dtype(const char *format) : dtype(std::string(format)) { }
|
||||
explicit dtype(const char *format) : dtype(std::string(format)) {}
|
||||
|
||||
dtype(list names, list formats, list offsets, ssize_t itemsize) {
|
||||
dict args;
|
||||
args["names"] = names;
|
||||
args["formats"] = formats;
|
||||
args["offsets"] = offsets;
|
||||
args["names"] = std::move(names);
|
||||
args["formats"] = std::move(formats);
|
||||
args["offsets"] = std::move(offsets);
|
||||
args["itemsize"] = pybind11::int_(itemsize);
|
||||
m_ptr = from_args(args).release().ptr();
|
||||
m_ptr = from_args(std::move(args)).release().ptr();
|
||||
}
|
||||
|
||||
/// This is essentially the same as calling numpy.dtype(args) in Python.
|
||||
static dtype from_args(object args) {
|
||||
PyObject *ptr = nullptr;
|
||||
if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr)
|
||||
if ((detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) == 0) || !ptr)
|
||||
throw error_already_set();
|
||||
return reinterpret_steal<dtype>(ptr);
|
||||
}
|
||||
@@ -507,11 +511,21 @@ public:
|
||||
return detail::array_descriptor_proxy(m_ptr)->names != nullptr;
|
||||
}
|
||||
|
||||
/// Single-character type code.
|
||||
/// Single-character code for dtype's kind.
|
||||
/// For example, floating point types are 'f' and integral types are 'i'.
|
||||
char kind() const {
|
||||
return detail::array_descriptor_proxy(m_ptr)->kind;
|
||||
}
|
||||
|
||||
/// Single-character for dtype's type.
|
||||
/// For example, ``float`` is 'f', ``double`` 'd', ``int`` 'i', and ``long`` 'l'.
|
||||
char char_() const {
|
||||
// Note: The signature, `dtype::char_` follows the naming of NumPy's
|
||||
// public Python API (i.e., ``dtype.char``), rather than its internal
|
||||
// C API (``PyArray_Descr::type``).
|
||||
return detail::array_descriptor_proxy(m_ptr)->type;
|
||||
}
|
||||
|
||||
private:
|
||||
static object _dtype_from_pep3118() {
|
||||
static PyObject *obj = module_::import("numpy.core._internal")
|
||||
@@ -533,7 +547,7 @@ private:
|
||||
auto name = spec[0].cast<pybind11::str>();
|
||||
auto format = spec[1].cast<tuple>()[0].cast<dtype>();
|
||||
auto offset = spec[1].cast<tuple>()[1].cast<pybind11::int_>();
|
||||
if (!len(name) && format.kind() == 'V')
|
||||
if ((len(name) == 0u) && format.kind() == 'V')
|
||||
continue;
|
||||
field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset});
|
||||
}
|
||||
@@ -549,7 +563,7 @@ private:
|
||||
formats.append(descr.format);
|
||||
offsets.append(descr.offset);
|
||||
}
|
||||
return dtype(names, formats, offsets, itemsize);
|
||||
return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -736,7 +750,7 @@ public:
|
||||
* and the caller must take care not to access invalid dimensions or dimension indices.
|
||||
*/
|
||||
template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
|
||||
if (Dims >= 0 && ndim() != Dims)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims)
|
||||
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
|
||||
"; expected " + std::to_string(Dims));
|
||||
return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim());
|
||||
@@ -750,7 +764,7 @@ public:
|
||||
* invalid dimensions or dimension indices.
|
||||
*/
|
||||
template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const & {
|
||||
if (Dims >= 0 && ndim() != Dims)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims)
|
||||
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
|
||||
"; expected " + std::to_string(Dims));
|
||||
return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
|
||||
@@ -779,6 +793,33 @@ public:
|
||||
if (isinstance<array>(new_array)) { *this = std::move(new_array); }
|
||||
}
|
||||
|
||||
/// Optional `order` parameter omitted, to be added as needed.
|
||||
array reshape(ShapeContainer new_shape) {
|
||||
detail::npy_api::PyArray_Dims d
|
||||
= {reinterpret_cast<Py_intptr_t *>(new_shape->data()), int(new_shape->size())};
|
||||
auto new_array
|
||||
= reinterpret_steal<array>(detail::npy_api::get().PyArray_Newshape_(m_ptr, &d, 0));
|
||||
if (!new_array) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return new_array;
|
||||
}
|
||||
|
||||
/// Create a view of an array in a different data type.
|
||||
/// This function may fundamentally reinterpret the data in the array.
|
||||
/// It is the responsibility of the caller to ensure that this is safe.
|
||||
/// Only supports the `dtype` argument, the `type` argument is omitted,
|
||||
/// to be added as needed.
|
||||
array view(const std::string &dtype) {
|
||||
auto &api = detail::npy_api::get();
|
||||
auto new_view = reinterpret_steal<array>(api.PyArray_View_(
|
||||
m_ptr, dtype::from_args(pybind11::str(dtype)).release().ptr(), nullptr));
|
||||
if (!new_view) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return new_view;
|
||||
}
|
||||
|
||||
/// Ensure that the argument is a NumPy array
|
||||
/// In case of an error, nullptr is returned and the Python error is cleared.
|
||||
static array ensure(handle h, int ExtraFlags = 0) {
|
||||
@@ -853,6 +894,7 @@ public:
|
||||
if (!is_borrowed) Py_XDECREF(h.ptr());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
|
||||
if (!m_ptr) throw error_already_set();
|
||||
}
|
||||
@@ -863,11 +905,12 @@ public:
|
||||
: array(std::move(shape), std::move(strides), ptr, base) { }
|
||||
|
||||
explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle())
|
||||
: array_t(private_ctor{}, std::move(shape),
|
||||
ExtraFlags & f_style
|
||||
? detail::f_strides(*shape, itemsize())
|
||||
: detail::c_strides(*shape, itemsize()),
|
||||
ptr, base) { }
|
||||
: array_t(private_ctor{},
|
||||
std::move(shape),
|
||||
(ExtraFlags & f_style) != 0 ? detail::f_strides(*shape, itemsize())
|
||||
: detail::c_strides(*shape, itemsize()),
|
||||
ptr,
|
||||
base) {}
|
||||
|
||||
explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle())
|
||||
: array({count}, {}, ptr, base) { }
|
||||
@@ -1020,7 +1063,10 @@ struct npy_format_descriptor_name<T, enable_if_t<std::is_integral<T>::value>> {
|
||||
|
||||
template <typename T>
|
||||
struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::value>> {
|
||||
static constexpr auto name = _<std::is_same<T, float>::value || std::is_same<T, double>::value>(
|
||||
static constexpr auto name = _<std::is_same<T, float>::value
|
||||
|| std::is_same<T, const float>::value
|
||||
|| std::is_same<T, double>::value
|
||||
|| std::is_same<T, const double>::value>(
|
||||
_("numpy.float") + _<sizeof(T)*8>(), _("numpy.longdouble")
|
||||
);
|
||||
};
|
||||
@@ -1028,7 +1074,9 @@ struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::valu
|
||||
template <typename T>
|
||||
struct npy_format_descriptor_name<T, enable_if_t<is_complex<T>::value>> {
|
||||
static constexpr auto name = _<std::is_same<typename T::value_type, float>::value
|
||||
|| std::is_same<typename T::value_type, double>::value>(
|
||||
|| std::is_same<typename T::value_type, const float>::value
|
||||
|| std::is_same<typename T::value_type, double>::value
|
||||
|| std::is_same<typename T::value_type, const double>::value>(
|
||||
_("numpy.complex") + _<sizeof(typename T::value_type)*16>(), _("numpy.longcomplex")
|
||||
);
|
||||
};
|
||||
@@ -1093,7 +1141,7 @@ struct field_descriptor {
|
||||
dtype descr;
|
||||
};
|
||||
|
||||
inline PYBIND11_NOINLINE void register_structured_dtype(
|
||||
PYBIND11_NOINLINE void register_structured_dtype(
|
||||
any_container<field_descriptor> fields,
|
||||
const std::type_info& tinfo, ssize_t itemsize,
|
||||
bool (*direct_converter)(PyObject *, void *&)) {
|
||||
@@ -1117,7 +1165,10 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
|
||||
formats.append(field.descr);
|
||||
offsets.append(pybind11::int_(field.offset));
|
||||
}
|
||||
auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr();
|
||||
auto dtype_ptr
|
||||
= pybind11::dtype(std::move(names), std::move(formats), std::move(offsets), itemsize)
|
||||
.release()
|
||||
.ptr();
|
||||
|
||||
// There is an existing bug in NumPy (as of v1.11): trailing bytes are
|
||||
// not encoded explicitly into the format string. This will supposedly
|
||||
@@ -1277,7 +1328,7 @@ public:
|
||||
using value_type = container_type::value_type;
|
||||
using size_type = container_type::size_type;
|
||||
|
||||
common_iterator() : p_ptr(0), m_strides() {}
|
||||
common_iterator() : m_strides() {}
|
||||
|
||||
common_iterator(void* ptr, const container_type& strides, const container_type& shape)
|
||||
: p_ptr(reinterpret_cast<char*>(ptr)), m_strides(strides.size()) {
|
||||
@@ -1298,7 +1349,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
char* p_ptr;
|
||||
char *p_ptr{0};
|
||||
container_type m_strides;
|
||||
};
|
||||
|
||||
@@ -1326,9 +1377,8 @@ public:
|
||||
if (++m_index[i] != m_shape[i]) {
|
||||
increment_common_iterator(i);
|
||||
break;
|
||||
} else {
|
||||
m_index[i] = 0;
|
||||
}
|
||||
m_index[i] = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -1479,8 +1529,7 @@ struct vectorize_returned_array {
|
||||
static Type create(broadcast_trivial trivial, const std::vector<ssize_t> &shape) {
|
||||
if (trivial == broadcast_trivial::f_trivial)
|
||||
return array_t<Return, array::f_style>(shape);
|
||||
else
|
||||
return array_t<Return>(shape);
|
||||
return array_t<Return>(shape);
|
||||
}
|
||||
|
||||
static Return *mutable_data(Type &array) {
|
||||
@@ -1536,8 +1585,11 @@ private:
|
||||
"pybind11::vectorize(...) requires a function with at least one vectorizable argument");
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) { }
|
||||
template <typename T,
|
||||
// SFINAE to prevent shadowing the copy constructor.
|
||||
typename = detail::enable_if_t<
|
||||
!std::is_same<vectorize_helper, typename std::decay<T>::type>::value>>
|
||||
explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) {}
|
||||
|
||||
object operator()(typename vectorize_arg<Args>::type... args) {
|
||||
return run(args...,
|
||||
@@ -1687,7 +1739,3 @@ Helper vectorize(Return (Class::*f)(Args...) const) {
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
@@ -11,13 +11,6 @@
|
||||
|
||||
#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
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
@@ -58,7 +51,8 @@ template <op_id id, op_type ot, typename L, typename R> struct op_ {
|
||||
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)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv) ||
|
||||
PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
|
||||
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
|
||||
&op::execute, is_operator(), extra...);
|
||||
#endif
|
||||
@@ -167,7 +161,3 @@ using detail::self;
|
||||
using detail::hash;
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,10 @@
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
# include <optional>
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
/* A few forward declarations */
|
||||
@@ -24,7 +28,7 @@ struct arg; struct arg_v;
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
class args_proxy;
|
||||
inline bool isinstance_generic(handle obj, const std::type_info &tp);
|
||||
bool isinstance_generic(handle obj, const std::type_info &tp);
|
||||
|
||||
// Accessor forward declarations
|
||||
template <typename Policy> class accessor;
|
||||
@@ -178,6 +182,7 @@ public:
|
||||
/// The default constructor creates a handle with a ``nullptr``-valued pointer
|
||||
handle() = default;
|
||||
/// Creates a ``handle`` from the given raw Python object pointer
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject*
|
||||
|
||||
/// Return the underlying ``PyObject *`` pointer
|
||||
@@ -254,8 +259,11 @@ public:
|
||||
|
||||
object& operator=(const object &other) {
|
||||
other.inc_ref();
|
||||
dec_ref();
|
||||
// Use temporary variable to ensure `*this` remains valid while
|
||||
// `Py_XDECREF` executes, in case `*this` is accessible from Python.
|
||||
handle temp(m_ptr);
|
||||
m_ptr = other.m_ptr;
|
||||
temp.dec_ref();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -279,8 +287,10 @@ protected:
|
||||
struct borrowed_t { };
|
||||
struct stolen_t { };
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Issue in breathe 4.26.1
|
||||
template <typename T> friend T reinterpret_borrow(handle);
|
||||
template <typename T> friend T reinterpret_steal(handle);
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Only accessible from derived classes and the reinterpret_* functions
|
||||
@@ -314,14 +324,18 @@ template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrow
|
||||
template <typename T> T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; }
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
inline std::string error_string();
|
||||
std::string error_string();
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4275 4251) // warning C4275: An exported class was derived from a class that wasn't exported. Can be ignored when derived from a STL class.
|
||||
#endif
|
||||
/// Fetch and hold an error which was already set in Python. An instance of this is typically
|
||||
/// thrown to propagate python-side errors back through C++ which can either be caught manually or
|
||||
/// else falls back to the function dispatcher (which then raises the captured error back to
|
||||
/// python).
|
||||
class error_already_set : public std::runtime_error {
|
||||
class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::runtime_error {
|
||||
public:
|
||||
/// Constructs a new exception from the current Python error indicator, if any. The current
|
||||
/// Python error indicator will be cleared.
|
||||
@@ -339,16 +353,17 @@ public:
|
||||
/// error variables (but the `.what()` string is still available).
|
||||
void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); }
|
||||
|
||||
/// If it is impossible to raise the currently-held error, such as in destructor, we can write
|
||||
/// it out using Python's unraisable hook (sys.unraisablehook). The error context should be
|
||||
/// some object whose repr() helps identify the location of the error. Python already knows the
|
||||
/// type and value of the error, so there is no need to repeat that. For example, __func__ could
|
||||
/// be helpful. After this call, the current object no longer stores the error variables,
|
||||
/// and neither does Python.
|
||||
/// If it is impossible to raise the currently-held error, such as in a destructor, we can write
|
||||
/// it out using Python's unraisable hook (`sys.unraisablehook`). The error context should be
|
||||
/// some object whose `repr()` helps identify the location of the error. Python already knows the
|
||||
/// type and value of the error, so there is no need to repeat that. After this call, the current
|
||||
/// object no longer stores the error variables, and neither does Python.
|
||||
void discard_as_unraisable(object err_context) {
|
||||
restore();
|
||||
PyErr_WriteUnraisable(err_context.ptr());
|
||||
}
|
||||
/// An alternate version of `discard_as_unraisable()`, where a string provides information on the
|
||||
/// location of the error. For example, `__func__` could be helpful.
|
||||
void discard_as_unraisable(const char *err_context) {
|
||||
discard_as_unraisable(reinterpret_steal<object>(PYBIND11_FROM_STRING(err_context)));
|
||||
}
|
||||
@@ -360,7 +375,9 @@ public:
|
||||
/// Check if the currently trapped error type matches the given Python exception class (or a
|
||||
/// subclass thereof). May also be passed a tuple to search for any exception class matches in
|
||||
/// the given tuple.
|
||||
bool matches(handle exc) const { return PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()); }
|
||||
bool matches(handle exc) const {
|
||||
return (PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()) != 0);
|
||||
}
|
||||
|
||||
const object& type() const { return m_type; }
|
||||
const object& value() const { return m_value; }
|
||||
@@ -369,6 +386,50 @@ public:
|
||||
private:
|
||||
object m_type, m_value, m_trace;
|
||||
};
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
|
||||
/// Replaces the current Python error indicator with the chosen error, performing a
|
||||
/// 'raise from' to indicate that the chosen error was caused by the original error.
|
||||
inline void raise_from(PyObject *type, const char *message) {
|
||||
// Based on _PyErr_FormatVFromCause:
|
||||
// https://github.com/python/cpython/blob/467ab194fc6189d9f7310c89937c51abeac56839/Python/errors.c#L405
|
||||
// See https://github.com/pybind/pybind11/pull/2112 for details.
|
||||
PyObject *exc = nullptr, *val = nullptr, *val2 = nullptr, *tb = nullptr;
|
||||
|
||||
assert(PyErr_Occurred());
|
||||
PyErr_Fetch(&exc, &val, &tb);
|
||||
PyErr_NormalizeException(&exc, &val, &tb);
|
||||
if (tb != nullptr) {
|
||||
PyException_SetTraceback(val, tb);
|
||||
Py_DECREF(tb);
|
||||
}
|
||||
Py_DECREF(exc);
|
||||
assert(!PyErr_Occurred());
|
||||
|
||||
PyErr_SetString(type, message);
|
||||
|
||||
PyErr_Fetch(&exc, &val2, &tb);
|
||||
PyErr_NormalizeException(&exc, &val2, &tb);
|
||||
Py_INCREF(val);
|
||||
PyException_SetCause(val2, val);
|
||||
PyException_SetContext(val2, val);
|
||||
PyErr_Restore(exc, val2, tb);
|
||||
}
|
||||
|
||||
/// Sets the current Python error indicator with the chosen error, performing a 'raise from'
|
||||
/// from the error contained in error_already_set to indicate that the chosen error was
|
||||
/// caused by the original error. After this function is called error_already_set will
|
||||
/// no longer contain an error.
|
||||
inline void raise_from(error_already_set& err, PyObject *type, const char *message) {
|
||||
err.restore();
|
||||
raise_from(type, message);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** \defgroup python_builtins _
|
||||
Unless stated otherwise, the following C++ functions behave the same
|
||||
@@ -431,19 +492,17 @@ inline object getattr(handle obj, const char *name) {
|
||||
inline object getattr(handle obj, handle name, handle default_) {
|
||||
if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) {
|
||||
return reinterpret_steal<object>(result);
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
PyErr_Clear();
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
|
||||
inline object getattr(handle obj, const char *name, handle default_) {
|
||||
if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) {
|
||||
return reinterpret_steal<object>(result);
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
PyErr_Clear();
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
|
||||
inline void setattr(handle obj, handle name, handle value) {
|
||||
@@ -476,6 +535,43 @@ inline handle get_function(handle value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Reimplementation of python's dict helper functions to ensure that exceptions
|
||||
// aren't swallowed (see #2862)
|
||||
|
||||
// copied from cpython _PyDict_GetItemStringWithError
|
||||
inline PyObject * dict_getitemstring(PyObject *v, const char *key)
|
||||
{
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *kv = nullptr, *rv = nullptr;
|
||||
kv = PyUnicode_FromString(key);
|
||||
if (kv == NULL) {
|
||||
throw error_already_set();
|
||||
}
|
||||
|
||||
rv = PyDict_GetItemWithError(v, kv);
|
||||
Py_DECREF(kv);
|
||||
if (rv == NULL && PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return rv;
|
||||
#else
|
||||
return PyDict_GetItemString(v, key);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline PyObject * dict_getitem(PyObject *v, PyObject *key)
|
||||
{
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *rv = PyDict_GetItemWithError(v, key);
|
||||
if (rv == NULL && PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
}
|
||||
return rv;
|
||||
#else
|
||||
return PyDict_GetItem(v, key);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Helper aliases/functions to support implicit casting of values given to python accessors/methods.
|
||||
// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes
|
||||
// through pybind11::cast(obj) to convert it to an `object`.
|
||||
@@ -487,6 +583,10 @@ object object_or_cast(T &&o);
|
||||
// Match a PyObject*, which we want to convert directly to handle via its converting constructor
|
||||
inline handle object_or_cast(PyObject *ptr) { return ptr; }
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1920
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified
|
||||
#endif
|
||||
template <typename Policy>
|
||||
class accessor : public object_api<accessor<Policy>> {
|
||||
using key_type = typename Policy::key_type;
|
||||
@@ -494,7 +594,7 @@ class accessor : public object_api<accessor<Policy>> {
|
||||
public:
|
||||
accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { }
|
||||
accessor(const accessor &) = default;
|
||||
accessor(accessor &&) = default;
|
||||
accessor(accessor &&) noexcept = default;
|
||||
|
||||
// accessor overload required to override default assignment operator (templates are not allowed
|
||||
// to replace default compiler-generated assignments).
|
||||
@@ -520,6 +620,7 @@ public:
|
||||
return obj.contains(key);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator object() const { return get_cache(); }
|
||||
PyObject *ptr() const { return get_cache().ptr(); }
|
||||
template <typename T> T cast() const { return get_cache().template cast<T>(); }
|
||||
@@ -535,6 +636,9 @@ private:
|
||||
key_type key;
|
||||
mutable object cache;
|
||||
};
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1920
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(accessor_policies)
|
||||
struct obj_attr {
|
||||
@@ -566,15 +670,17 @@ struct generic_item {
|
||||
struct sequence_item {
|
||||
using key_type = size_t;
|
||||
|
||||
static object get(handle obj, size_t index) {
|
||||
PyObject *result = PySequence_GetItem(obj.ptr(), static_cast<ssize_t>(index));
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static object get(handle obj, const IdxType &index) {
|
||||
PyObject *result = PySequence_GetItem(obj.ptr(), ssize_t_cast(index));
|
||||
if (!result) { throw error_already_set(); }
|
||||
return reinterpret_steal<object>(result);
|
||||
}
|
||||
|
||||
static void set(handle obj, size_t index, handle val) {
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static void set(handle obj, const IdxType &index, handle val) {
|
||||
// PySequence_SetItem does not steal a reference to 'val'
|
||||
if (PySequence_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.ptr()) != 0) {
|
||||
if (PySequence_SetItem(obj.ptr(), ssize_t_cast(index), val.ptr()) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
@@ -583,15 +689,17 @@ struct sequence_item {
|
||||
struct list_item {
|
||||
using key_type = size_t;
|
||||
|
||||
static object get(handle obj, size_t index) {
|
||||
PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index));
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static object get(handle obj, const IdxType &index) {
|
||||
PyObject *result = PyList_GetItem(obj.ptr(), ssize_t_cast(index));
|
||||
if (!result) { throw error_already_set(); }
|
||||
return reinterpret_borrow<object>(result);
|
||||
}
|
||||
|
||||
static void set(handle obj, size_t index, handle val) {
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static void set(handle obj, const IdxType &index, handle val) {
|
||||
// PyList_SetItem steals a reference to 'val'
|
||||
if (PyList_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
|
||||
if (PyList_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
@@ -600,15 +708,17 @@ struct list_item {
|
||||
struct tuple_item {
|
||||
using key_type = size_t;
|
||||
|
||||
static object get(handle obj, size_t index) {
|
||||
PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index));
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static object get(handle obj, const IdxType &index) {
|
||||
PyObject *result = PyTuple_GetItem(obj.ptr(), ssize_t_cast(index));
|
||||
if (!result) { throw error_already_set(); }
|
||||
return reinterpret_borrow<object>(result);
|
||||
}
|
||||
|
||||
static void set(handle obj, size_t index, handle val) {
|
||||
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
static void set(handle obj, const IdxType &index, handle val) {
|
||||
// PyTuple_SetItem steals a reference to 'val'
|
||||
if (PyTuple_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) {
|
||||
if (PyTuple_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
|
||||
throw error_already_set();
|
||||
}
|
||||
}
|
||||
@@ -630,7 +740,9 @@ public:
|
||||
generic_iterator() = default;
|
||||
generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { }
|
||||
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
reference operator*() const { return Policy::dereference(); }
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
reference operator[](difference_type n) const { return *(*this + n); }
|
||||
pointer operator->() const { return **this; }
|
||||
|
||||
@@ -660,7 +772,8 @@ template <typename T>
|
||||
struct arrow_proxy {
|
||||
T value;
|
||||
|
||||
arrow_proxy(T &&value) : value(std::move(value)) { }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
arrow_proxy(T &&value) noexcept : value(std::move(value)) { }
|
||||
T *operator->() const { return &value; }
|
||||
};
|
||||
|
||||
@@ -669,11 +782,12 @@ class sequence_fast_readonly {
|
||||
protected:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = handle;
|
||||
using reference = const handle;
|
||||
using reference = const handle; // PR #3263
|
||||
using pointer = arrow_proxy<const handle>;
|
||||
|
||||
sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { }
|
||||
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
reference dereference() const { return *ptr; }
|
||||
void increment() { ++ptr; }
|
||||
void decrement() { --ptr; }
|
||||
@@ -712,14 +826,19 @@ class dict_readonly {
|
||||
protected:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = std::pair<handle, handle>;
|
||||
using reference = const value_type;
|
||||
using reference = const value_type; // PR #3263
|
||||
using pointer = arrow_proxy<const value_type>;
|
||||
|
||||
dict_readonly() = default;
|
||||
dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); }
|
||||
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
reference dereference() const { return {key, value}; }
|
||||
void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } }
|
||||
void increment() {
|
||||
if (PyDict_Next(obj.ptr(), &pos, &key, &value) == 0) {
|
||||
pos = -1;
|
||||
}
|
||||
}
|
||||
bool equal(const dict_readonly &b) const { return pos == b.pos; }
|
||||
|
||||
private:
|
||||
@@ -745,16 +864,20 @@ inline bool PyIterable_Check(PyObject *obj) {
|
||||
if (iter) {
|
||||
Py_DECREF(iter);
|
||||
return true;
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool PyNone_Check(PyObject *o) { return o == Py_None; }
|
||||
inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; }
|
||||
|
||||
#ifdef PYBIND11_STR_LEGACY_PERMISSIVE
|
||||
inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); }
|
||||
#define PYBIND11_STR_CHECK_FUN detail::PyUnicode_Check_Permissive
|
||||
#else
|
||||
#define PYBIND11_STR_CHECK_FUN PyUnicode_Check
|
||||
#endif
|
||||
|
||||
inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; }
|
||||
|
||||
@@ -797,21 +920,28 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \
|
||||
Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \
|
||||
PYBIND11_DEPRECATED("Use py::isinstance<py::python_type>(obj) instead") \
|
||||
bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \
|
||||
bool check() const { return m_ptr != nullptr && (CheckFun(m_ptr) != 0); } \
|
||||
static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } \
|
||||
template <typename Policy_> \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(const ::pybind11::detail::accessor<Policy_> &a) : Name(object(a)) { }
|
||||
|
||||
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
|
||||
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
|
||||
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(const object &o) \
|
||||
: Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
|
||||
{ if (!m_ptr) throw error_already_set(); } \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(object &&o) \
|
||||
: Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
|
||||
{ if (!m_ptr) throw error_already_set(); }
|
||||
|
||||
#define PYBIND11_OBJECT_CVT_DEFAULT(Name, Parent, CheckFun, ConvertFun) \
|
||||
PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
|
||||
Name() : Parent() { }
|
||||
|
||||
#define PYBIND11_OBJECT_CHECK_FAILED(Name, o_ptr) \
|
||||
::pybind11::type_error("Object of type '" + \
|
||||
::pybind11::detail::get_fully_qualified_tp_name(Py_TYPE(o_ptr)) + \
|
||||
@@ -820,8 +950,10 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
#define PYBIND11_OBJECT(Name, Parent, CheckFun) \
|
||||
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
|
||||
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(const object &o) : Parent(o) \
|
||||
{ if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); } \
|
||||
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
|
||||
Name(object &&o) : Parent(std::move(o)) \
|
||||
{ if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); }
|
||||
|
||||
@@ -845,7 +977,7 @@ public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using difference_type = ssize_t;
|
||||
using value_type = handle;
|
||||
using reference = const handle;
|
||||
using reference = const handle; // PR #3263
|
||||
using pointer = const handle *;
|
||||
|
||||
PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check)
|
||||
@@ -861,6 +993,7 @@ public:
|
||||
return rv;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
|
||||
reference operator*() const {
|
||||
if (m_ptr && !value.ptr()) {
|
||||
auto& self = const_cast<iterator &>(*this);
|
||||
@@ -934,19 +1067,22 @@ class bytes;
|
||||
|
||||
class str : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str)
|
||||
PYBIND11_OBJECT_CVT(str, object, PYBIND11_STR_CHECK_FUN, raw_str)
|
||||
|
||||
str(const char *c, size_t n)
|
||||
: object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
|
||||
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
str(const char *c, const SzType &n)
|
||||
: object(PyUnicode_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate string object!");
|
||||
}
|
||||
|
||||
// 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
str(const char *c = "")
|
||||
: object(PyUnicode_FromString(c), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate string object!");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
str(const std::string &s) : str(s.data(), s.size()) { }
|
||||
|
||||
explicit str(const bytes &b);
|
||||
@@ -957,15 +1093,16 @@ public:
|
||||
\endrst */
|
||||
explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); }
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::string() const {
|
||||
object temp = *this;
|
||||
if (PyUnicode_Check(m_ptr)) {
|
||||
temp = reinterpret_steal<object>(PyUnicode_AsUTF8String(m_ptr));
|
||||
if (!temp)
|
||||
pybind11_fail("Unable to extract string contents! (encoding issue)");
|
||||
throw error_already_set();
|
||||
}
|
||||
char *buffer;
|
||||
ssize_t length;
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
|
||||
pybind11_fail("Unable to extract string contents! (invalid type)");
|
||||
return std::string(buffer, (size_t) length);
|
||||
@@ -1004,24 +1141,28 @@ public:
|
||||
PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK)
|
||||
|
||||
// Allow implicit conversion:
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
bytes(const char *c = "")
|
||||
: object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
|
||||
}
|
||||
|
||||
bytes(const char *c, size_t n)
|
||||
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) {
|
||||
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
bytes(const char *c, const SzType &n)
|
||||
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, ssize_t_cast(n)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
|
||||
}
|
||||
|
||||
// Allow implicit conversion:
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
bytes(const std::string &s) : bytes(s.data(), s.size()) { }
|
||||
|
||||
explicit bytes(const pybind11::str &s);
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator std::string() const {
|
||||
char *buffer;
|
||||
ssize_t length;
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length))
|
||||
pybind11_fail("Unable to extract bytes contents!");
|
||||
return std::string(buffer, (size_t) length);
|
||||
@@ -1038,8 +1179,8 @@ inline bytes::bytes(const pybind11::str &s) {
|
||||
if (!temp)
|
||||
pybind11_fail("Unable to extract string contents! (encoding issue)");
|
||||
}
|
||||
char *buffer;
|
||||
ssize_t length;
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length))
|
||||
pybind11_fail("Unable to extract string contents! (invalid type)");
|
||||
auto obj = reinterpret_steal<object>(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length));
|
||||
@@ -1049,16 +1190,45 @@ inline bytes::bytes(const pybind11::str &s) {
|
||||
}
|
||||
|
||||
inline str::str(const bytes& b) {
|
||||
char *buffer;
|
||||
ssize_t length;
|
||||
char *buffer = nullptr;
|
||||
ssize_t length = 0;
|
||||
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
|
||||
pybind11_fail("Unable to extract bytes contents!");
|
||||
auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, (ssize_t) length));
|
||||
auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, length));
|
||||
if (!obj)
|
||||
pybind11_fail("Could not allocate string object!");
|
||||
m_ptr = obj.release().ptr();
|
||||
}
|
||||
|
||||
/// \addtogroup pytypes
|
||||
/// @{
|
||||
class bytearray : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(bytearray, object, PyByteArray_Check, PyByteArray_FromObject)
|
||||
|
||||
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
bytearray(const char *c, const SzType &n)
|
||||
: object(PyByteArray_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate bytearray object!");
|
||||
}
|
||||
|
||||
bytearray()
|
||||
: bytearray("", 0) {}
|
||||
|
||||
explicit bytearray(const std::string &s) : bytearray(s.data(), s.size()) { }
|
||||
|
||||
size_t size() const { return static_cast<size_t>(PyByteArray_Size(m_ptr)); }
|
||||
|
||||
explicit operator std::string() const {
|
||||
char *buffer = PyByteArray_AS_STRING(m_ptr);
|
||||
ssize_t size = PyByteArray_GET_SIZE(m_ptr);
|
||||
return std::string(buffer, static_cast<size_t>(size));
|
||||
}
|
||||
};
|
||||
// Note: breathe >= 4.17.0 will fail to build docs if the below two constructors
|
||||
// are included in the doxygen group; close here and reopen after as a workaround
|
||||
/// @} pytypes
|
||||
|
||||
/// \addtogroup pytypes
|
||||
/// @{
|
||||
class none : public object {
|
||||
@@ -1078,15 +1248,17 @@ public:
|
||||
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool)
|
||||
bool_() : object(Py_False, borrowed_t{}) { }
|
||||
// Allow implicit conversion from and to `bool`:
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { }
|
||||
operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator bool() const { return (m_ptr != nullptr) && PyLong_AsLong(m_ptr) != 0; }
|
||||
|
||||
private:
|
||||
/// Return the truth value of an object -- always returns a new reference
|
||||
static PyObject *raw_bool(PyObject *op) {
|
||||
const auto value = PyObject_IsTrue(op);
|
||||
if (value == -1) return nullptr;
|
||||
return handle(value ? Py_True : Py_False).inc_ref().ptr();
|
||||
return handle(value != 0 ? Py_True : Py_False).inc_ref().ptr();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1097,18 +1269,16 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes).
|
||||
template <typename Unsigned>
|
||||
Unsigned as_unsigned(PyObject *o) {
|
||||
if (sizeof(Unsigned) <= sizeof(unsigned long)
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long))
|
||||
#if PY_VERSION_HEX < 0x03000000
|
||||
|| PyInt_Check(o)
|
||||
|| PyInt_Check(o)
|
||||
#endif
|
||||
) {
|
||||
unsigned long v = PyLong_AsUnsignedLong(o);
|
||||
return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
|
||||
}
|
||||
else {
|
||||
unsigned long long v = PyLong_AsUnsignedLongLong(o);
|
||||
return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
|
||||
}
|
||||
unsigned long long v = PyLong_AsUnsignedLongLong(o);
|
||||
return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
|
||||
}
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
@@ -1119,8 +1289,9 @@ public:
|
||||
// Allow implicit conversion from C++ integral types:
|
||||
template <typename T,
|
||||
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
int_(T value) {
|
||||
if (sizeof(T) <= sizeof(long)) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(T) <= sizeof(long))) {
|
||||
if (std::is_signed<T>::value)
|
||||
m_ptr = PyLong_FromLong((long) value);
|
||||
else
|
||||
@@ -1136,6 +1307,7 @@ public:
|
||||
|
||||
template <typename T,
|
||||
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator T() const {
|
||||
return std::is_unsigned<T>::value
|
||||
? detail::as_unsigned<T>(m_ptr)
|
||||
@@ -1149,33 +1321,51 @@ class float_ : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float)
|
||||
// Allow implicit conversion from float/double:
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate float object!");
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate float object!");
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator double() const { return (double) PyFloat_AsDouble(m_ptr); }
|
||||
};
|
||||
|
||||
class weakref : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check)
|
||||
PYBIND11_OBJECT_CVT_DEFAULT(weakref, object, PyWeakref_Check, raw_weakref)
|
||||
explicit weakref(handle obj, handle callback = {})
|
||||
: object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate weak reference!");
|
||||
}
|
||||
|
||||
private:
|
||||
static PyObject *raw_weakref(PyObject *o) {
|
||||
return PyWeakref_NewRef(o, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
class slice : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check)
|
||||
slice(ssize_t start_, ssize_t stop_, ssize_t step_) {
|
||||
int_ start(start_), stop(stop_), step(step_);
|
||||
slice(handle start, handle stop, handle step) {
|
||||
m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr());
|
||||
if (!m_ptr) pybind11_fail("Could not allocate slice object!");
|
||||
if (!m_ptr)
|
||||
pybind11_fail("Could not allocate slice object!");
|
||||
}
|
||||
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
slice(std::optional<ssize_t> start, std::optional<ssize_t> stop, std::optional<ssize_t> step)
|
||||
: slice(index_to_object(start), index_to_object(stop), index_to_object(step)) {}
|
||||
#else
|
||||
slice(ssize_t start_, ssize_t stop_, ssize_t step_)
|
||||
: slice(int_(start_), int_(stop_), int_(step_)) {}
|
||||
#endif
|
||||
|
||||
bool compute(size_t length, size_t *start, size_t *stop, size_t *step,
|
||||
size_t *slicelength) const {
|
||||
return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr,
|
||||
@@ -1190,6 +1380,12 @@ public:
|
||||
stop, step,
|
||||
slicelength) == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static object index_to_object(T index) {
|
||||
return index ? object(int_(*index)) : object(none());
|
||||
}
|
||||
};
|
||||
|
||||
class capsule : public object {
|
||||
@@ -1225,7 +1421,7 @@ public:
|
||||
pybind11_fail("Could not set capsule context!");
|
||||
}
|
||||
|
||||
capsule(void (*destructor)()) {
|
||||
explicit capsule(void (*destructor)()) {
|
||||
m_ptr = PyCapsule_New(reinterpret_cast<void *>(destructor), nullptr, [](PyObject *o) {
|
||||
auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, nullptr));
|
||||
destructor();
|
||||
@@ -1235,6 +1431,7 @@ public:
|
||||
pybind11_fail("Could not allocate capsule object!");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
template <typename T> operator T *() const {
|
||||
return get_pointer<T>();
|
||||
}
|
||||
@@ -1244,14 +1441,19 @@ public:
|
||||
T* get_pointer() const {
|
||||
auto name = this->name();
|
||||
T *result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, name));
|
||||
if (!result) pybind11_fail("Unable to extract capsule contents!");
|
||||
if (!result) {
|
||||
PyErr_Clear();
|
||||
pybind11_fail("Unable to extract capsule contents!");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Replaces a capsule's pointer *without* calling the destructor on the existing one.
|
||||
void set_pointer(const void *value) {
|
||||
if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0)
|
||||
if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0) {
|
||||
PyErr_Clear();
|
||||
pybind11_fail("Could not set capsule pointer");
|
||||
}
|
||||
}
|
||||
|
||||
const char *name() const { return PyCapsule_GetName(m_ptr); }
|
||||
@@ -1260,7 +1462,10 @@ public:
|
||||
class tuple : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple)
|
||||
explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) {
|
||||
template <typename SzType = ssize_t,
|
||||
detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
// Some compilers generate link errors when using `const SzType &` here:
|
||||
explicit tuple(SzType size = 0) : object(PyTuple_New(ssize_t_cast(size)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
|
||||
}
|
||||
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
|
||||
@@ -1296,7 +1501,7 @@ public:
|
||||
bool empty() const { return size() == 0; }
|
||||
detail::dict_iterator begin() const { return {*this, 0}; }
|
||||
detail::dict_iterator end() const { return {}; }
|
||||
void clear() const { PyDict_Clear(ptr()); }
|
||||
void clear() /* py-non-const */ { PyDict_Clear(ptr()); }
|
||||
template <typename T> bool contains(T &&key) const {
|
||||
return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
|
||||
}
|
||||
@@ -1329,7 +1534,10 @@ public:
|
||||
class list : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List)
|
||||
explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) {
|
||||
template <typename SzType = ssize_t,
|
||||
detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
|
||||
// Some compilers generate link errors when using `const SzType &` here:
|
||||
explicit list(SzType size = 0) : object(PyList_New(ssize_t_cast(size)), stolen_t{}) {
|
||||
if (!m_ptr) pybind11_fail("Could not allocate list object!");
|
||||
}
|
||||
size_t size() const { return (size_t) PyList_Size(m_ptr); }
|
||||
@@ -1338,12 +1546,15 @@ public:
|
||||
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
|
||||
detail::list_iterator begin() const { return {*this, 0}; }
|
||||
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
|
||||
template <typename T> void append(T &&val) const {
|
||||
template <typename T> void append(T &&val) /* py-non-const */ {
|
||||
PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
|
||||
}
|
||||
template <typename T> void insert(size_t index, T &&val) const {
|
||||
PyList_Insert(m_ptr, static_cast<ssize_t>(index),
|
||||
detail::object_or_cast(std::forward<T>(val)).ptr());
|
||||
template <typename IdxType,
|
||||
typename ValType,
|
||||
detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
|
||||
void insert(const IdxType &index, ValType &&val) /* py-non-const */ {
|
||||
PyList_Insert(
|
||||
m_ptr, ssize_t_cast(index), detail::object_or_cast(std::forward<ValType>(val)).ptr());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1358,10 +1569,10 @@ public:
|
||||
}
|
||||
size_t size() const { return (size_t) PySet_Size(m_ptr); }
|
||||
bool empty() const { return size() == 0; }
|
||||
template <typename T> bool add(T &&val) const {
|
||||
template <typename T> bool add(T &&val) /* py-non-const */ {
|
||||
return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
|
||||
}
|
||||
void clear() const { PySet_Clear(m_ptr); }
|
||||
void clear() /* py-non-const */ { PySet_Clear(m_ptr); }
|
||||
template <typename T> bool contains(T &&val) const {
|
||||
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
|
||||
}
|
||||
@@ -1457,7 +1668,7 @@ public:
|
||||
detail::any_container<ssize_t> shape,
|
||||
detail::any_container<ssize_t> strides) {
|
||||
return memoryview::from_buffer(
|
||||
const_cast<void*>(ptr), itemsize, format, shape, strides, true);
|
||||
const_cast<void *>(ptr), itemsize, format, std::move(shape), std::move(strides), true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -1514,7 +1725,7 @@ inline memoryview memoryview::from_buffer(
|
||||
size_t ndim = shape->size();
|
||||
if (ndim != strides->size())
|
||||
pybind11_fail("memoryview: shape length doesn't match strides length");
|
||||
ssize_t size = ndim ? 1 : 0;
|
||||
ssize_t size = ndim != 0u ? 1 : 0;
|
||||
for (size_t i = 0; i < ndim; ++i)
|
||||
size *= (*shape)[i];
|
||||
Py_buffer view;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "pybind11.h"
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
@@ -19,33 +20,15 @@
|
||||
#include <deque>
|
||||
#include <valarray>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
||||
// See `detail/common.h` for implementation of these guards.
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
# include <optional>
|
||||
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
|
||||
# include <experimental/optional>
|
||||
#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>) && \
|
||||
!__has_include(<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>
|
||||
#if defined(PYBIND11_HAS_VARIANT)
|
||||
# include <variant>
|
||||
# define PYBIND11_HAS_OPTIONAL 1
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
@@ -144,7 +127,7 @@ template <typename Type, typename Value> struct list_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<sequence>(src) || isinstance<str>(src))
|
||||
if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src))
|
||||
return false;
|
||||
auto s = reinterpret_borrow<sequence>(src);
|
||||
value.clear();
|
||||
@@ -159,10 +142,13 @@ template <typename Type, typename Value> struct list_caster {
|
||||
}
|
||||
|
||||
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 *) { }
|
||||
template <
|
||||
typename T = Type,
|
||||
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
|
||||
void reserve_maybe(const sequence &s, Type *) {
|
||||
value.reserve(s.size());
|
||||
}
|
||||
void reserve_maybe(const sequence &, void *) {}
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
@@ -170,12 +156,12 @@ public:
|
||||
if (!std::is_lvalue_reference<T>::value)
|
||||
policy = return_value_policy_override<Value>::policy(policy);
|
||||
list l(src.size());
|
||||
size_t index = 0;
|
||||
ssize_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
|
||||
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
|
||||
}
|
||||
return l.release();
|
||||
}
|
||||
@@ -227,12 +213,12 @@ public:
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
list l(src.size());
|
||||
size_t index = 0;
|
||||
ssize_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
|
||||
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
|
||||
}
|
||||
return l.release();
|
||||
}
|
||||
@@ -259,34 +245,35 @@ template <typename Key, typename Value, typename Hash, typename Equal, typename
|
||||
: 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 Type, typename Value = typename Type::value_type> struct optional_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
template <typename T_>
|
||||
static handle cast(T_ &&src, return_value_policy policy, handle parent) {
|
||||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
if (!src)
|
||||
return none().inc_ref();
|
||||
if (!std::is_lvalue_reference<T>::value) {
|
||||
policy = return_value_policy_override<T>::policy(policy);
|
||||
policy = return_value_policy_override<Value>::policy(policy);
|
||||
}
|
||||
return value_conv::cast(*std::forward<T_>(src), policy, parent);
|
||||
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()) {
|
||||
}
|
||||
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)));
|
||||
value.emplace(cast_op<Value &&>(std::move(inner_caster)));
|
||||
return true;
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]"));
|
||||
PYBIND11_TYPE_CASTER(Type, _("Optional[") + value_conv::name + _("]"));
|
||||
};
|
||||
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
@@ -377,12 +364,12 @@ struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> {
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
|
||||
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||
os << str(obj).cast<std::string_view>();
|
||||
#else
|
||||
os << (std::string) str(obj);
|
||||
#endif
|
||||
return os;
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
103
ext/pybind11/include/pybind11/stl/filesystem.h
Normal file
103
ext/pybind11/include/pybind11/stl/filesystem.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2021 The Pybind Development Team.
|
||||
// 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"
|
||||
#include "../pybind11.h"
|
||||
#include "../pytypes.h"
|
||||
|
||||
#include "../detail/common.h"
|
||||
#include "../detail/descr.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef __has_include
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \
|
||||
PY_VERSION_HEX >= 0x03060000
|
||||
# include <filesystem>
|
||||
# define PYBIND11_HAS_FILESYSTEM 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
|
||||
# error \
|
||||
"#include <filesystem> is not available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)"
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if defined(PYBIND11_HAS_FILESYSTEM)
|
||||
template<typename T> struct path_caster {
|
||||
|
||||
private:
|
||||
static PyObject* unicode_from_fs_native(const std::string& w) {
|
||||
#if !defined(PYPY_VERSION)
|
||||
return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size()));
|
||||
#else
|
||||
// PyPy mistakenly declares the first parameter as non-const.
|
||||
return PyUnicode_DecodeFSDefaultAndSize(
|
||||
const_cast<char*>(w.c_str()), ssize_t(w.size()));
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject* unicode_from_fs_native(const std::wstring& w) {
|
||||
return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size()));
|
||||
}
|
||||
|
||||
public:
|
||||
static handle cast(const T& path, return_value_policy, handle) {
|
||||
if (auto py_str = unicode_from_fs_native(path.native())) {
|
||||
return module_::import("pathlib").attr("Path")(reinterpret_steal<object>(py_str))
|
||||
.release();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool load(handle handle, bool) {
|
||||
// PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of
|
||||
// calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy
|
||||
// issue #3168) so we do it ourselves instead.
|
||||
PyObject* buf = PyOS_FSPath(handle.ptr());
|
||||
if (!buf) {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
PyObject* native = nullptr;
|
||||
if constexpr (std::is_same_v<typename T::value_type, char>) {
|
||||
if (PyUnicode_FSConverter(buf, &native) != 0) {
|
||||
if (auto c_str = PyBytes_AsString(native)) {
|
||||
// AsString returns a pointer to the internal buffer, which
|
||||
// must not be free'd.
|
||||
value = c_str;
|
||||
}
|
||||
}
|
||||
} else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) {
|
||||
if (PyUnicode_FSDecoder(buf, &native) != 0) {
|
||||
if (auto c_str = PyUnicode_AsWideCharString(native, nullptr)) {
|
||||
// AsWideCharString returns a new string that must be free'd.
|
||||
value = c_str; // Copies the string.
|
||||
PyMem_Free(c_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
Py_XDECREF(native);
|
||||
Py_DECREF(buf);
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(T, _("os.PathLike"));
|
||||
};
|
||||
|
||||
template<> struct type_caster<std::filesystem::path>
|
||||
: public path_caster<std::filesystem::path> {};
|
||||
#endif // PYBIND11_HAS_FILESYSTEM
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
@@ -128,11 +128,11 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
arg("x"),
|
||||
"Add an item to the end of the list");
|
||||
|
||||
cl.def(init([](iterable it) {
|
||||
cl.def(init([](const iterable &it) {
|
||||
auto v = std::unique_ptr<Vector>(new Vector());
|
||||
v->reserve(len_hint(it));
|
||||
for (handle h : it)
|
||||
v->push_back(h.cast<T>());
|
||||
v->push_back(h.cast<T>());
|
||||
return v.release();
|
||||
}));
|
||||
|
||||
@@ -151,27 +151,28 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
"Extend the list by appending all the items in the given list"
|
||||
);
|
||||
|
||||
cl.def("extend",
|
||||
[](Vector &v, iterable it) {
|
||||
const size_t old_size = v.size();
|
||||
v.reserve(old_size + len_hint(it));
|
||||
try {
|
||||
for (handle h : it) {
|
||||
v.push_back(h.cast<T>());
|
||||
}
|
||||
} catch (const cast_error &) {
|
||||
v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size), v.end());
|
||||
try {
|
||||
v.shrink_to_fit();
|
||||
} catch (const std::exception &) {
|
||||
// Do nothing
|
||||
}
|
||||
throw;
|
||||
}
|
||||
},
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list"
|
||||
);
|
||||
cl.def(
|
||||
"extend",
|
||||
[](Vector &v, const iterable &it) {
|
||||
const size_t old_size = v.size();
|
||||
v.reserve(old_size + len_hint(it));
|
||||
try {
|
||||
for (handle h : it) {
|
||||
v.push_back(h.cast<T>());
|
||||
}
|
||||
} catch (const cast_error &) {
|
||||
v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size),
|
||||
v.end());
|
||||
try {
|
||||
v.shrink_to_fit();
|
||||
} catch (const std::exception &) {
|
||||
// Do nothing
|
||||
}
|
||||
throw;
|
||||
}
|
||||
},
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list");
|
||||
|
||||
cl.def("insert",
|
||||
[](Vector &v, DiffType i, const T &x) {
|
||||
@@ -190,7 +191,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
[](Vector &v) {
|
||||
if (v.empty())
|
||||
throw index_error();
|
||||
T t = v.back();
|
||||
T t = std::move(v.back());
|
||||
v.pop_back();
|
||||
return t;
|
||||
},
|
||||
@@ -200,8 +201,8 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
cl.def("pop",
|
||||
[wrap_i](Vector &v, DiffType i) {
|
||||
i = wrap_i(i, v.size());
|
||||
T t = v[(SizeType) i];
|
||||
v.erase(v.begin() + i);
|
||||
T t = std::move(v[(SizeType) i]);
|
||||
v.erase(std::next(v.begin(), i));
|
||||
return t;
|
||||
},
|
||||
arg("i"),
|
||||
@@ -216,9 +217,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
);
|
||||
|
||||
/// Slicing protocol
|
||||
cl.def("__getitem__",
|
||||
cl.def(
|
||||
"__getitem__",
|
||||
[](const Vector &v, slice slice) -> Vector * {
|
||||
size_t start, stop, step, slicelength;
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
|
||||
throw error_already_set();
|
||||
@@ -233,12 +235,12 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
return seq;
|
||||
},
|
||||
arg("s"),
|
||||
"Retrieve list elements using a slice object"
|
||||
);
|
||||
"Retrieve list elements using a slice object");
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Vector &v, slice slice, const Vector &value) {
|
||||
size_t start, stop, step, slicelength;
|
||||
cl.def(
|
||||
"__setitem__",
|
||||
[](Vector &v, slice slice, const Vector &value) {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
|
||||
throw error_already_set();
|
||||
|
||||
@@ -250,8 +252,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
start += step;
|
||||
}
|
||||
},
|
||||
"Assign list elements using a slice object"
|
||||
);
|
||||
"Assign list elements using a slice object");
|
||||
|
||||
cl.def("__delitem__",
|
||||
[wrap_i](Vector &v, DiffType i) {
|
||||
@@ -261,9 +262,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
"Delete the list elements at index ``i``"
|
||||
);
|
||||
|
||||
cl.def("__delitem__",
|
||||
cl.def(
|
||||
"__delitem__",
|
||||
[](Vector &v, slice slice) {
|
||||
size_t start, stop, step, slicelength;
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength))
|
||||
throw error_already_set();
|
||||
@@ -277,9 +279,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
}
|
||||
}
|
||||
},
|
||||
"Delete list elements using a slice object"
|
||||
);
|
||||
|
||||
"Delete list elements using a slice object");
|
||||
}
|
||||
|
||||
// If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>),
|
||||
@@ -400,7 +400,7 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
|
||||
return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)});
|
||||
});
|
||||
|
||||
cl.def(init([](buffer buf) {
|
||||
cl.def(init([](const 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");
|
||||
@@ -413,13 +413,12 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
|
||||
if (step == 1) {
|
||||
return Vector(p, end);
|
||||
}
|
||||
else {
|
||||
Vector vec;
|
||||
vec.reserve((size_t) info.shape[0]);
|
||||
for (; p != end; p += step)
|
||||
vec.push_back(*p);
|
||||
return vec;
|
||||
}
|
||||
Vector vec;
|
||||
vec.reserve((size_t) info.shape[0]);
|
||||
for (; p != end; p += step)
|
||||
vec.push_back(*p);
|
||||
return vec;
|
||||
|
||||
}));
|
||||
|
||||
return;
|
||||
@@ -596,6 +595,23 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
|
||||
);
|
||||
}
|
||||
|
||||
template<typename Map>
|
||||
struct keys_view
|
||||
{
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template<typename Map>
|
||||
struct values_view
|
||||
{
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template<typename Map>
|
||||
struct items_view
|
||||
{
|
||||
Map ↦
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
@@ -603,6 +619,9 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename...
|
||||
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&... args) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
using KeysView = detail::keys_view<Map>;
|
||||
using ValuesView = detail::values_view<Map>;
|
||||
using ItemsView = detail::items_view<Map>;
|
||||
using Class_ = class_<Map, holder_type>;
|
||||
|
||||
// If either type is a non-module-local bound type then make the map binding non-local as well;
|
||||
@@ -616,6 +635,12 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
}
|
||||
|
||||
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
|
||||
class_<KeysView> keys_view(
|
||||
scope, ("KeysView[" + name + "]").c_str(), pybind11::module_local(local));
|
||||
class_<ValuesView> values_view(
|
||||
scope, ("ValuesView[" + name + "]").c_str(), pybind11::module_local(local));
|
||||
class_<ItemsView> items_view(
|
||||
scope, ("ItemsView[" + name + "]").c_str(), pybind11::module_local(local));
|
||||
|
||||
cl.def(init<>());
|
||||
|
||||
@@ -629,12 +654,22 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
|
||||
);
|
||||
|
||||
cl.def("keys",
|
||||
[](Map &m) { return KeysView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("values",
|
||||
[](Map &m) { return ValuesView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("items",
|
||||
[](Map &m) { return make_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
[](Map &m) { return ItemsView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("__getitem__",
|
||||
@@ -655,6 +690,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
return true;
|
||||
}
|
||||
);
|
||||
// Fallback for when the object is not of the key type
|
||||
cl.def("__contains__", [](Map &, const object &) -> bool { return false; });
|
||||
|
||||
// Assignment provided only if the type is copyable
|
||||
detail::map_assignment<Map, Class_>(cl);
|
||||
@@ -670,6 +707,40 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
|
||||
cl.def("__len__", &Map::size);
|
||||
|
||||
keys_view.def("__len__", [](KeysView &view) { return view.map.size(); });
|
||||
keys_view.def("__iter__",
|
||||
[](KeysView &view) {
|
||||
return make_key_iterator(view.map.begin(), view.map.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
keys_view.def("__contains__",
|
||||
[](KeysView &view, const KeyType &k) -> bool {
|
||||
auto it = view.map.find(k);
|
||||
if (it == view.map.end())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
// Fallback for when the object is not of the key type
|
||||
keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; });
|
||||
|
||||
values_view.def("__len__", [](ValuesView &view) { return view.map.size(); });
|
||||
values_view.def("__iter__",
|
||||
[](ValuesView &view) {
|
||||
return make_value_iterator(view.map.begin(), view.map.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
|
||||
items_view.def("__len__", [](ItemsView &view) { return view.map.size(); });
|
||||
items_view.def("__iter__",
|
||||
[](ItemsView &view) {
|
||||
return make_iterator(view.map.begin(), view.map.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user