## pybind11
For simplicity, the pybind headers were retrieved from
-https://github.com/pybind/pybind11/archive/v2.2.4.tar.gz
+https://github.com/pybind/pybind11/archive/v2.4.3.tar.gz
git rm -rf pybind
mkdir -p pybind
- wget https://github.com/pybind/pybind11/archive/v2.2.4.tar.gz
+ wget https://github.com/pybind/pybind11/archive/v2.4.3.tar.gz
tar xvf *.tar.gz
mv pybind11*/include pybind/
mv pybind11*/tools pybind/
/// Special data structure which (temporarily) holds metadata about a bound class
struct type_record {
PYBIND11_NOINLINE type_record()
- : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), module_local(false) { }
+ : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false),
+ default_holder(true), module_local(false) { }
/// Handle to the parent scope
handle scope;
/// How large is the underlying C++ type?
size_t type_size = 0;
+ /// What is the alignment of the underlying C++ type?
+ size_t type_align = 0;
+
/// How large is the type's holder?
size_t holder_size = 0;
/// The global operator new can be overridden with a class-specific variant
- void *(*operator_new)(size_t) = ::operator new;
+ void *(*operator_new)(size_t) = nullptr;
/// Function pointer to class_<..>::init_instance
void (*init_instance)(instance *, const void *) = nullptr;
}
};
-inline function_call::function_call(function_record &f, handle p) :
+inline function_call::function_call(const function_record &f, handle p) :
func(f), parent(p) {
args.reserve(f.nargs);
args_convert.reserve(f.nargs);
#include <array>
#include <limits>
#include <tuple>
+#include <type_traits>
#if defined(PYBIND11_CPP17)
# if defined(__has_include)
}
struct value_and_holder {
- instance *inst;
- size_t index;
- const detail::type_info *type;
- void **vh;
+ 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) :
{}
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
- value_and_holder() : inst{nullptr} {}
+ value_and_holder() {}
// Used for past-the-end iterator
value_and_holder(size_t index) : index{index} {}
struct iterator {
private:
- instance *inst;
- const type_vec *types;
+ instance *inst = nullptr;
+ const type_vec *types = nullptr;
value_and_holder curr;
friend struct values_and_holders;
iterator(instance *inst, const type_vec *tinfo)
// Lazy allocation for unallocated values:
if (vptr == nullptr) {
auto *type = v_h.type ? v_h.type : typeinfo;
- vptr = type->operator_new(type->type_size);
+ if (type->operator_new) {
+ vptr = type->operator_new(type->type_size);
+ } else {
+ #if defined(PYBIND11_CPP17)
+ 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;
}
// 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>
+ 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> {};
#if !defined(PYBIND11_CPP17)
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
#endif
+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.)
+template <typename itype, typename SFINAE = void>
+struct polymorphic_type_hook
+{
+ static const void *get(const itype *src, const std::type_info*&) { return src; }
+};
+template <typename itype>
+struct polymorphic_type_hook<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);
+ }
+};
+
+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 PYBIND11_DESCR name() { return type_descr(_<type>()); }
+ 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) { }
return cast(&src, return_value_policy::move, parent);
}
- // Returns a (pointer, type_info) pair taking care of necessary RTTI type lookup for a
- // polymorphic type. If the instance isn't derived, returns the non-RTTI base version.
- template <typename T = itype, enable_if_t<std::is_polymorphic<T>::value, int> = 0>
+ // 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) {
- const void *vsrc = src;
auto &cast_type = typeid(itype);
const std::type_info *instance_type = nullptr;
- if (vsrc) {
- instance_type = &typeid(*src);
- if (!same_type(cast_type, *instance_type)) {
- // This is a base pointer to a derived type; if it is a pybind11-registered type, we
- // can get the correct derived pointer (which may be != base pointer) by a
- // dynamic_cast to most derived type:
- if (auto *tpi = get_type_info(*instance_type))
- return {dynamic_cast<const void *>(src), const_cast<const type_info *>(tpi)};
- }
+ 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(vsrc, cast_type, instance_type);
- }
-
- // Non-polymorphic type, so no dynamic casting; just call the generic version directly
- template <typename T = itype, enable_if_t<!std::is_polymorphic<T>::value, int> = 0>
- static std::pair<const void *, const type_info *> src_and_type(const itype *src) {
- return type_caster_generic::src_and_type(src, typeid(itype));
+ return type_caster_generic::src_and_type(src, cast_type, instance_type);
}
static handle cast(const itype *src, return_value_policy policy, handle parent) {
nullptr, nullptr, holder);
}
- template <typename T> using cast_op_type = cast_op_type<T>;
+ template <typename T> using cast_op_type = detail::cast_op_type<T>;
operator itype*() { return (type *) value; }
operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); }
"std::reference_wrapper<T> caster requires T to have a caster with an `T &` operator");
public:
bool load(handle src, bool convert) { return subcaster.load(src, convert); }
- static PYBIND11_DESCR name() { return caster_t::name(); }
+ static constexpr auto name = caster_t::name;
static handle cast(const std::reference_wrapper<type> &src, return_value_policy policy, handle parent) {
// It is definitely wrong to take ownership of this pointer, so mask that rvp
if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic)
protected: \
type value; \
public: \
- static PYBIND11_DESCR name() { return type_descr(py_name); } \
+ static constexpr auto name = py_name; \
template <typename T_, enable_if_t<std::is_same<type, remove_cv_t<T_>>::value, int> = 0> \
static handle cast(T_ *src, return_value_policy policy, handle parent) { \
if (!src) return none().release(); \
}
bool py_err = py_value == (py_type) -1 && PyErr_Occurred();
+
+ // Protect std::numeric_limits::min/max with parentheses
if (py_err || (std::is_integral<T>::value && sizeof(py_type) != sizeof(T) &&
- (py_value < (py_type) std::numeric_limits<T>::min() ||
- py_value > (py_type) std::numeric_limits<T>::max()))) {
+ (py_value < (py_type) (std::numeric_limits<T>::min)() ||
+ py_value > (py_type) (std::numeric_limits<T>::max)()))) {
bool type_error = py_err && PyErr_ExceptionMatches(
#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION)
PyExc_SystemError
return true;
}
- static handle cast(T src, return_value_policy /* policy */, handle /* parent */) {
- if (std::is_floating_point<T>::value) {
- return PyFloat_FromDouble((double) src);
- } else if (sizeof(T) <= sizeof(long)) {
- if (std::is_signed<T>::value)
- return PyLong_FromLong((long) src);
- else
- return PyLong_FromUnsignedLong((unsigned long) src);
- } else {
- if (std::is_signed<T>::value)
- return PyLong_FromLongLong((long long) src);
- else
- return PyLong_FromUnsignedLongLong((unsigned long long) src);
- }
+ template<typename U = T>
+ static typename std::enable_if<std::is_floating_point<U>::value, handle>::type
+ cast(U src, return_value_policy /* policy */, handle /* parent */) {
+ return PyFloat_FromDouble((double) src);
+ }
+
+ template<typename U = T>
+ static typename std::enable_if<!std::is_floating_point<U>::value && std::is_signed<U>::value && (sizeof(U) <= sizeof(long)), handle>::type
+ cast(U src, return_value_policy /* policy */, handle /* parent */) {
+ return PYBIND11_LONG_FROM_SIGNED((long) src);
+ }
+
+ template<typename U = T>
+ static typename std::enable_if<!std::is_floating_point<U>::value && std::is_unsigned<U>::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type
+ cast(U src, return_value_policy /* policy */, handle /* parent */) {
+ return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src);
+ }
+
+ template<typename U = T>
+ static typename std::enable_if<!std::is_floating_point<U>::value && std::is_signed<U>::value && (sizeof(U) > sizeof(long)), handle>::type
+ cast(U src, return_value_policy /* policy */, handle /* parent */) {
+ return PyLong_FromLongLong((long long) src);
+ }
+
+ template<typename U = T>
+ static typename std::enable_if<!std::is_floating_point<U>::value && std::is_unsigned<U>::value && (sizeof(U) > sizeof(unsigned long)), handle>::type
+ cast(U src, return_value_policy /* policy */, handle /* parent */) {
+ return PyLong_FromUnsignedLongLong((unsigned long long) src);
}
PYBIND11_TYPE_CASTER(T, _<std::is_integral<T>::value>("int", "float"));
template <typename T> using cast_op_type = void*&;
operator void *&() { return value; }
- static PYBIND11_DESCR name() { return type_descr(_("capsule")); }
+ static constexpr auto name = _("capsule");
private:
void *value = nullptr;
};
return one_char;
}
- static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); }
+ static constexpr auto name = _(PYBIND11_STRING_NAME);
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
};
return cast_impl(std::forward<T>(src), policy, parent, indices{});
}
- static PYBIND11_DESCR name() {
- return type_descr(_("Tuple[") + detail::concat(make_caster<Ts>::name()...) + _("]"));
- }
+ static constexpr auto name = _("Tuple[") + concat(make_caster<Ts>::name...) + _("]");
template <typename T> using cast_op_type = type;
auto *ptr = holder_helper<holder_type>::get(src);
return type_caster_base<type>::cast_holder(ptr, std::addressof(src));
}
- static PYBIND11_DESCR name() { return type_caster_base<type>::name(); }
+ static constexpr auto name = type_caster_base<type>::name;
};
template <typename type, typename deleter>
template <typename base, typename deleter> struct is_holder_type<base, std::unique_ptr<base, deleter>> :
std::true_type {};
-template <typename T> struct handle_type_name { static PYBIND11_DESCR name() { return _<T>(); } };
-template <> struct handle_type_name<bytes> { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } };
-template <> struct handle_type_name<args> { static PYBIND11_DESCR name() { return _("*args"); } };
-template <> struct handle_type_name<kwargs> { static PYBIND11_DESCR name() { return _("**kwargs"); } };
+template <typename T> struct handle_type_name { static constexpr auto name = _<T>(); };
+template <> struct handle_type_name<bytes> { static constexpr auto name = _(PYBIND11_BYTES_NAME); };
+template <> struct handle_type_name<args> { static constexpr auto name = _("*args"); };
+template <> struct handle_type_name<kwargs> { static constexpr auto name = _("**kwargs"); };
template <typename type>
struct pyobject_caster {
static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
return src.inc_ref();
}
- PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name());
+ PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
};
template <typename T>
// everything else returns a reference/pointer to a local variable.
template <typename type> using cast_is_temporary_value_reference = bool_constant<
(std::is_reference<type>::value || std::is_pointer<type>::value) &&
- !std::is_base_of<type_caster_generic, make_caster<type>>::value
+ !std::is_base_of<type_caster_generic, make_caster<type>>::value &&
+ !std::is_same<intrinsic_t<type>, void>::value
>;
// When a value returned from a C++ function is being cast back to Python, we almost always want to
template <typename Return> struct return_value_policy_override<Return,
detail::enable_if_t<std::is_base_of<type_caster_generic, make_caster<Return>>::value, void>> {
static return_value_policy policy(return_value_policy p) {
- return !std::is_lvalue_reference<Return>::value && !std::is_pointer<Return>::value
- ? return_value_policy::move : p;
+ return !std::is_lvalue_reference<Return>::value &&
+ !std::is_pointer<Return>::value
+ ? return_value_policy::move : p;
}
};
/// Internal data associated with a single function call
struct function_call {
- function_call(function_record &f, handle p); // Implementation in attr.h
+ function_call(const function_record &f, handle p); // Implementation in attr.h
/// The function data:
const function_record &func;
static constexpr bool has_kwargs = kwargs_pos < 0;
static constexpr bool has_args = args_pos < 0;
- static PYBIND11_DESCR arg_names() { return detail::concat(make_caster<Args>::name()...); }
+ static constexpr auto arg_names = concat(type_descr(make_caster<Args>::name)...);
bool load_args(function_call &call) {
return load_impl_sequence(call, indices{});
NAMESPACE_END(detail)
-#define PYBIND11_MAKE_OPAQUE(Type) \
+#define PYBIND11_MAKE_OPAQUE(...) \
namespace pybind11 { namespace detail { \
- template<> class type_caster<Type> : public type_caster_base<Type> { }; \
+ template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \
}}
+/// Lets you pass a type containing a `,` through a macro parameter without needing a separate
+/// typedef, e.g.: `PYBIND11_OVERLOAD(PYBIND11_TYPE(ReturnType<A, B>), PYBIND11_TYPE(Parent<C, D>), f, arg)`
+#define PYBIND11_TYPE(...) __VA_ARGS__
+
NAMESPACE_END(PYBIND11_NAMESPACE)
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
if (!src) return false;
+
+ std::tm cal;
+ microseconds msecs;
+
if (PyDateTime_Check(src.ptr())) {
- std::tm cal;
cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
cal.tm_isdst = -1;
-
- value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
- return true;
+ msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
+ } else if (PyDate_Check(src.ptr())) {
+ cal.tm_sec = 0;
+ cal.tm_min = 0;
+ cal.tm_hour = 0;
+ cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
+ cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
+ cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
+ cal.tm_isdst = -1;
+ msecs = microseconds(0);
+ } else if (PyTime_Check(src.ptr())) {
+ cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
+ cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
+ cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
+ cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
+ cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
+ cal.tm_year = 70; // earliest available date for Python's datetime
+ cal.tm_isdst = -1;
+ msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
}
else return false;
+
+ value = system_clock::from_time_t(std::mktime(&cal)) + msecs;
+ return true;
}
static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, return_value_policy /* policy */, handle /* parent */) {
// Lazy initialise the PyDateTime import
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
- std::time_t tt = system_clock::to_time_t(src);
+ std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src));
// this function uses static memory so it's best to copy it out asap just in case
// otherwise other code that is using localtime may break this (not just python code)
std::tm localtime = *std::localtime(&tt);
static std::string format() { return std::string(value); }
};
+#ifndef PYBIND11_CPP17
+
template <typename T> constexpr const char format_descriptor<
std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
+#endif
+
NAMESPACE_BEGIN(detail)
template <typename T> struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
#pragma once
#include "../attr.h"
+#include "../options.h"
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
NAMESPACE_BEGIN(detail)
inline void add_patient(PyObject *nurse, PyObject *patient) {
auto &internals = get_internals();
auto instance = reinterpret_cast<detail::instance *>(nurse);
- auto ¤t_patients = internals.patients[nurse];
instance->has_patients = true;
- for (auto &p : current_patients)
- if (p == patient)
- return;
Py_INCREF(patient);
- current_patients.push_back(patient);
+ internals.patients[nurse].push_back(patient);
}
inline void clear_patients(PyObject *self) {
auto type = Py_TYPE(self);
type->tp_free(self);
+#if PY_VERSION_HEX < 0x03080000
// `type->tp_dealloc != pybind11_object_dealloc` means that we're being called
// as part of a derived type's dealloc, in which case we're not allowed to decref
// the type here. For cross-module compatibility, we shouldn't compare directly
auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base;
if (type->tp_dealloc == pybind11_object_type->tp_dealloc)
Py_DECREF(type);
+#else
+ // This was not needed before Python 3.8 (Python issue 35810)
+ // https://github.com/pybind/pybind11/issues/1946
+ Py_DECREF(type);
+#endif
}
/** Create the type which can be used as a common base for all classes. This is
if (tinfo && tinfo->get_buffer)
break;
}
- if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) {
+ if (view == nullptr || !tinfo || !tinfo->get_buffer) {
if (view)
view->obj = nullptr;
PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
type->tp_as_number = &heap_type->as_number;
type->tp_as_sequence = &heap_type->as_sequence;
type->tp_as_mapping = &heap_type->as_mapping;
+#if PY_VERSION_HEX >= 0x03050000
+ type->tp_as_async = &heap_type->as_async;
+#endif
/* Flags */
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
#endif
#define PYBIND11_VERSION_MAJOR 2
-#define PYBIND11_VERSION_MINOR 2
-#define PYBIND11_VERSION_PATCH 4
+#define PYBIND11_VERSION_MINOR 4
+#define PYBIND11_VERSION_PATCH 3
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
#if defined(_MSC_VER)
#include <frameobject.h>
#include <pythread.h>
-#if defined(_WIN32) && (defined(min) || defined(max))
-# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows
-#endif
-
#if defined(isalnum)
# undef isalnum
# undef isalpha
#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_BYTES_NAME "bytes"
#define PYBIND11_STRING_NAME "str"
#define PYBIND11_SLICE_OBJECT PyObject
#define PYBIND11_STR_TYPE ::pybind11::str
#define PYBIND11_BOOL_ATTR "__bool__"
#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool)
+// Providing a separate declaration to make Clang's -Wmissing-prototypes happy
#define PYBIND11_PLUGIN_IMPL(name) \
+ extern "C" PYBIND11_EXPORT PyObject *PyInit_##name(); \
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
#else
#define PYBIND11_BYTES_SIZE PyString_Size
#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o))
+#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed.
+#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed.
#define PYBIND11_BYTES_NAME "str"
#define PYBIND11_STRING_NAME "unicode"
#define PYBIND11_SLICE_OBJECT PySliceObject
#define PYBIND11_STR_TYPE ::pybind11::bytes
#define PYBIND11_BOOL_ATTR "__nonzero__"
#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero)
+// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy
#define PYBIND11_PLUGIN_IMPL(name) \
static PyObject *pybind11_init_wrapper(); \
+ extern "C" PYBIND11_EXPORT void init##name(); \
extern "C" PYBIND11_EXPORT void init##name() { \
(void)pybind11_init_wrapper(); \
} \
#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x)
#define PYBIND11_CONCAT(first, second) first##second
+#define PYBIND11_CHECK_PYTHON_VERSION \
+ { \
+ const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \
+ "." PYBIND11_TOSTRING(PY_MINOR_VERSION); \
+ const char *runtime_ver = Py_GetVersion(); \
+ size_t len = std::strlen(compiled_ver); \
+ if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \
+ || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \
+ PyErr_Format(PyExc_ImportError, \
+ "Python version mismatch: module was compiled for Python %s, " \
+ "but the interpreter version is incompatible: %s.", \
+ compiled_ver, runtime_ver); \
+ return nullptr; \
+ } \
+ }
+
+#define PYBIND11_CATCH_INIT_EXCEPTIONS \
+ catch (pybind11::error_already_set &e) { \
+ PyErr_SetString(PyExc_ImportError, e.what()); \
+ return nullptr; \
+ } catch (const std::exception &e) { \
+ PyErr_SetString(PyExc_ImportError, e.what()); \
+ return nullptr; \
+ } \
+
/** \rst
***Deprecated in favor of PYBIND11_MODULE***
PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \
static PyObject *pybind11_init(); \
PYBIND11_PLUGIN_IMPL(name) { \
- int major, minor; \
- if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \
- PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \
- return nullptr; \
- } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \
- PyErr_Format(PyExc_ImportError, \
- "Python version mismatch: module was compiled for " \
- "version %i.%i, while the interpreter is running " \
- "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \
- major, minor); \
- return nullptr; \
- } \
+ PYBIND11_CHECK_PYTHON_VERSION \
try { \
return pybind11_init(); \
- } catch (pybind11::error_already_set &e) { \
- PyErr_SetString(PyExc_ImportError, e.what()); \
- return nullptr; \
- } catch (const std::exception &e) { \
- PyErr_SetString(PyExc_ImportError, e.what()); \
- return nullptr; \
- } \
+ } PYBIND11_CATCH_INIT_EXCEPTIONS \
} \
PyObject *pybind11_init()
#define PYBIND11_MODULE(name, variable) \
static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \
PYBIND11_PLUGIN_IMPL(name) { \
- int major, minor; \
- if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \
- PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \
- return nullptr; \
- } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \
- PyErr_Format(PyExc_ImportError, \
- "Python version mismatch: module was compiled for " \
- "version %i.%i, while the interpreter is running " \
- "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \
- major, minor); \
- return nullptr; \
- } \
+ PYBIND11_CHECK_PYTHON_VERSION \
auto m = pybind11::module(PYBIND11_TOSTRING(name)); \
try { \
PYBIND11_CONCAT(pybind11_init_, name)(m); \
return m.ptr(); \
- } catch (pybind11::error_already_set &e) { \
- PyErr_SetString(PyExc_ImportError, e.what()); \
- return nullptr; \
- } catch (const std::exception &e) { \
- PyErr_SetString(PyExc_ImportError, e.what()); \
- return nullptr; \
- } \
+ } PYBIND11_CATCH_INIT_EXCEPTIONS \
} \
void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable)
void *simple_value_holder[1 + instance_simple_holder_in_ptrs()];
nonsimple_values_and_holders nonsimple;
};
- /// Weak references (needed for keep alive):
+ /// Weak references
PyObject *weakrefs;
/// If true, the pointer is owned which means we're free to manage it with a holder.
bool owned : 1;
* (which is typically the size of two pointers), or when multiple inheritance is used on the
* python side. Non-simple layout allocates the required amount of memory to have multiple
* bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a
- * pointer to allocated space of the required space to hold a a sequence of value pointers and
+ * pointer to allocated space of the required space to hold a sequence of value pointers and
* holders followed `status`, a set of bit flags (1 byte each), i.e.
* [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of
- * `sizeof(void *)`. `nonsimple.holder_constructed` is, for convenience, a pointer to the
+ * `sizeof(void *)`. `nonsimple.status` is, for convenience, a pointer to the
* beginning of the [bb...] block (but not independently allocated).
*
* Status bits indicate whether the associated holder is constructed (&
template <typename Base, typename Derived> using is_strict_base_of = bool_constant<
std::is_base_of<Base, Derived>::value && !std::is_same<Base, Derived>::value>;
+/// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer
+/// can be converted to a Base pointer)
+template <typename Base, typename Derived> using is_accessible_base_of = bool_constant<
+ std::is_base_of<Base, Derived>::value && std::is_convertible<Derived *, Base *>::value>;
+
template <template<typename...> class Base>
struct is_template_base_of_impl {
template <typename... Us> static std::true_type check(Base<Us...> *);
PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError)
PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError)
PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError)
+PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError)
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
static std::string format() { return std::string(1, c); }
};
+#if !defined(PYBIND11_CPP17)
+
template <typename T> constexpr const char format_descriptor<
T, detail::enable_if_t<std::is_arithmetic<T>::value>>::value[2];
+#endif
+
/// RAII wrapper that temporarily clears any Python error state
struct error_scope {
PyObject *type, *value, *trace;
/// Dummy destructor wrapper that can be used to expose classes with a private destructor
struct nodelete { template <typename T> void operator()(T*) { } };
-// overload_cast requires variable templates: C++14
-#if defined(PYBIND11_CPP14)
-#define PYBIND11_OVERLOAD_CAST 1
-
NAMESPACE_BEGIN(detail)
template <typename... Args>
struct overload_cast_impl {
};
NAMESPACE_END(detail)
+// overload_cast requires variable templates: C++14
+#if defined(PYBIND11_CPP14)
+#define PYBIND11_OVERLOAD_CAST 1
/// Syntax sugar for resolving overloaded function pointers:
/// - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
/// - sweet: overload_cast<Arg0, Arg1, Arg2>(&Class::func)
template <typename... Args>
static constexpr detail::overload_cast_impl<Args...> overload_cast = {};
// MSVC 2015 only accepts this particular initialization syntax for this variable template.
+#endif
/// Const member function selector for overload_cast
/// - regular: static_cast<Return (Class::*)(Arg) const>(&Class::func)
/// - sweet: overload_cast<Arg>(&Class::func, const_)
static constexpr auto const_ = std::true_type{};
-#else // no overload_cast: providing something that static_assert-fails:
+#if !defined(PYBIND11_CPP14) // no overload_cast: providing something that static_assert-fails:
template <typename... Args> struct overload_cast {
static_assert(detail::deferred_t<std::false_type, Args...>::value,
"pybind11::overload_cast<...> requires compiling in C++14 mode");
/*
- pybind11/detail/descr.h: Helper type for concatenating type signatures
- either at runtime (C++11) or compile time (C++14)
+ pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
NAMESPACE_BEGIN(detail)
-/* Concatenate type signatures at compile time using C++14 */
-#if defined(PYBIND11_CPP14) && !defined(_MSC_VER)
-#define PYBIND11_CONSTEXPR_DESCR
-
-template <size_t Size1, size_t Size2> class descr {
- template <size_t Size1_, size_t Size2_> friend class descr;
-public:
- constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1])
- : descr(text, types,
- make_index_sequence<Size1>(),
- make_index_sequence<Size2>()) { }
-
- constexpr const char *text() const { return m_text; }
- constexpr const std::type_info * const * types() const { return m_types; }
-
- template <size_t OtherSize1, size_t OtherSize2>
- constexpr descr<Size1 + OtherSize1, Size2 + OtherSize2> operator+(const descr<OtherSize1, OtherSize2> &other) const {
- return concat(other,
- make_index_sequence<Size1>(),
- make_index_sequence<Size2>(),
- make_index_sequence<OtherSize1>(),
- make_index_sequence<OtherSize2>());
- }
+#if !defined(_MSC_VER)
+# define PYBIND11_DESCR_CONSTEXPR static constexpr
+#else
+# define PYBIND11_DESCR_CONSTEXPR const
+#endif
-protected:
- template <size_t... Indices1, size_t... Indices2>
- constexpr descr(
- char const (&text) [Size1+1],
- const std::type_info * const (&types) [Size2+1],
- index_sequence<Indices1...>, index_sequence<Indices2...>)
- : m_text{text[Indices1]..., '\0'},
- m_types{types[Indices2]..., nullptr } {}
-
- template <size_t OtherSize1, size_t OtherSize2, size_t... Indices1,
- size_t... Indices2, size_t... OtherIndices1, size_t... OtherIndices2>
- constexpr descr<Size1 + OtherSize1, Size2 + OtherSize2>
- concat(const descr<OtherSize1, OtherSize2> &other,
- index_sequence<Indices1...>, index_sequence<Indices2...>,
- index_sequence<OtherIndices1...>, index_sequence<OtherIndices2...>) const {
- return descr<Size1 + OtherSize1, Size2 + OtherSize2>(
- { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' },
- { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr }
- );
- }
+/* Concatenate type signatures at compile time */
+template <size_t N, typename... Ts>
+struct descr {
+ char text[N + 1];
+
+ constexpr descr() : text{'\0'} { }
+ 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'} { }
-protected:
- char m_text[Size1 + 1];
- const std::type_info * m_types[Size2 + 1];
+ template <typename... Chars>
+ 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() {
+ return {{&typeid(Ts)..., nullptr}};
+ }
};
-template <size_t Size> constexpr descr<Size - 1, 0> _(char const(&text)[Size]) {
- return descr<Size - 1, 0>(text, { nullptr });
+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...>) {
+ return {a.text[Is1]..., b.text[Is2]...};
+}
+
+template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
+constexpr descr<N1 + N2, Ts1..., Ts2...> operator+(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b) {
+ return plus_impl(a, b, make_index_sequence<N1>(), make_index_sequence<N2>());
}
+template <size_t N>
+constexpr descr<N - 1> _(char const(&text)[N]) { return descr<N - 1>(text); }
+constexpr descr<0> _(char const(&)[1]) { return {}; }
+
template <size_t Rem, size_t... Digits> struct int_to_str : int_to_str<Rem/10, Rem%10, Digits...> { };
template <size_t...Digits> struct int_to_str<0, Digits...> {
- static constexpr auto digits = descr<sizeof...(Digits), 0>({ ('0' + Digits)..., '\0' }, { nullptr });
+ static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...);
};
// Ternary description (like std::conditional)
-template <bool B, size_t Size1, size_t Size2>
-constexpr enable_if_t<B, descr<Size1 - 1, 0>> _(char const(&text1)[Size1], char const(&)[Size2]) {
+template <bool B, size_t N1, size_t N2>
+constexpr enable_if_t<B, descr<N1 - 1>> _(char const(&text1)[N1], char const(&)[N2]) {
return _(text1);
}
-template <bool B, size_t Size1, size_t Size2>
-constexpr enable_if_t<!B, descr<Size2 - 1, 0>> _(char const(&)[Size1], char const(&text2)[Size2]) {
+template <bool B, size_t N1, size_t N2>
+constexpr enable_if_t<!B, descr<N2 - 1>> _(char const(&)[N1], char const(&text2)[N2]) {
return _(text2);
}
-template <bool B, size_t SizeA1, size_t SizeA2, size_t SizeB1, size_t SizeB2>
-constexpr enable_if_t<B, descr<SizeA1, SizeA2>> _(descr<SizeA1, SizeA2> d, descr<SizeB1, SizeB2>) { return d; }
-template <bool B, size_t SizeA1, size_t SizeA2, size_t SizeB1, size_t SizeB2>
-constexpr enable_if_t<!B, descr<SizeB1, SizeB2>> _(descr<SizeA1, SizeA2>, descr<SizeB1, SizeB2> d) { return d; }
+
+template <bool B, typename T1, typename T2>
+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) {
return int_to_str<Size / 10, Size % 10>::digits;
}
-template <typename Type> constexpr descr<1, 1> _() {
- return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr });
-}
-
-inline constexpr descr<0, 0> concat() { return _(""); }
-template <size_t Size1, size_t Size2, typename... Args> auto constexpr concat(descr<Size1, Size2> descr) { return descr; }
-template <size_t Size1, size_t Size2, typename... Args> auto constexpr concat(descr<Size1, Size2> descr, Args&&... args) { return descr + _(", ") + concat(args...); }
-template <size_t Size1, size_t Size2> auto constexpr type_descr(descr<Size1, Size2> descr) { return _("{") + descr + _("}"); }
-
-#define PYBIND11_DESCR constexpr auto
-
-#else /* Simpler C++11 implementation based on run-time memory allocation and copying */
-
-class descr {
-public:
- PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) {
- size_t nChars = len(text), nTypes = len(types);
- m_text = new char[nChars];
- m_types = new const std::type_info *[nTypes];
- memcpy(m_text, text, nChars * sizeof(char));
- memcpy(m_types, types, nTypes * sizeof(const std::type_info *));
- }
-
- PYBIND11_NOINLINE descr operator+(descr &&d2) && {
- descr r;
-
- size_t nChars1 = len(m_text), nTypes1 = len(m_types);
- size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types);
-
- r.m_text = new char[nChars1 + nChars2 - 1];
- r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1];
- memcpy(r.m_text, m_text, (nChars1-1) * sizeof(char));
- memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char));
- memcpy(r.m_types, m_types, (nTypes1-1) * sizeof(std::type_info *));
- memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *));
+template <typename Type> constexpr descr<1, Type> _() { return {'%'}; }
- delete[] m_text; delete[] m_types;
- delete[] d2.m_text; delete[] d2.m_types;
-
- return r;
- }
+constexpr descr<0> concat() { return {}; }
- char *text() { return m_text; }
- const std::type_info * * types() { return m_types; }
+template <size_t N, typename... Ts>
+constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) { return descr; }
-protected:
- PYBIND11_NOINLINE descr() { }
-
- template <typename T> static size_t len(const T *ptr) { // return length including null termination
- const T *it = ptr;
- while (*it++ != (T) 0)
- ;
- return static_cast<size_t>(it - ptr);
- }
-
- const std::type_info **m_types = nullptr;
- char *m_text = nullptr;
-};
-
-/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */
-
-PYBIND11_NOINLINE inline descr _(const char *text) {
- const std::type_info *types[1] = { nullptr };
- return descr(text, types);
-}
-
-template <bool B> PYBIND11_NOINLINE enable_if_t<B, descr> _(const char *text1, const char *) { return _(text1); }
-template <bool B> PYBIND11_NOINLINE enable_if_t<!B, descr> _(char const *, const char *text2) { return _(text2); }
-template <bool B> PYBIND11_NOINLINE enable_if_t<B, descr> _(descr d, descr) { return d; }
-template <bool B> PYBIND11_NOINLINE enable_if_t<!B, descr> _(descr, descr d) { return d; }
-
-template <typename Type> PYBIND11_NOINLINE descr _() {
- const std::type_info *types[2] = { &typeid(Type), nullptr };
- return descr("%", types);
+template <size_t N, typename... Ts, typename... Args>
+constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
+ -> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
+ return d + _(", ") + concat(args...);
}
-template <size_t Size> PYBIND11_NOINLINE descr _() {
- const std::type_info *types[1] = { nullptr };
- return descr(std::to_string(Size).c_str(), types);
+template <size_t N, typename... Ts>
+constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
+ return _("{") + descr + _("}");
}
-PYBIND11_NOINLINE inline descr concat() { return _(""); }
-PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; }
-template <typename... Args> PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward<Args>(args)...); }
-PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); }
-
-#define PYBIND11_DESCR ::pybind11::detail::descr
-#endif
-
NAMESPACE_END(detail)
NAMESPACE_END(PYBIND11_NAMESPACE)
template <typename> using cast_op_type = value_and_holder &;
operator value_and_holder &() { return *value; }
- static PYBIND11_DESCR name() { return type_descr(_<value_and_holder>()); }
+ static constexpr auto name = _<value_and_holder>();
private:
value_and_holder *value = nullptr;
#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), (tstate))
+# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
#else
// Usually an int but a long on Cygwin64 with Python 3.x
struct type_info {
PyTypeObject *type;
const std::type_info *cpptype;
- size_t type_size, holder_size_in_ptrs;
+ size_t type_size, type_align, holder_size_in_ptrs;
void *(*operator_new)(size_t);
void (*init_instance)(instance *, const void *);
void (*dealloc)(value_and_holder &v_h);
};
/// Tracks the `internals` and `type_info` ABI version independent of the main library version
-#define PYBIND11_INTERNALS_VERSION 2
+#define PYBIND11_INTERNALS_VERSION 3
+
+/// On MSVC, debug and release builds are not ABI-compatible!
+#if defined(_MSC_VER) && defined(_DEBUG)
+# define PYBIND11_BUILD_TYPE "_debug"
+#else
+# define PYBIND11_BUILD_TYPE ""
+#endif
+
+/// Let's assume that different compilers are ABI-incompatible.
+#if defined(_MSC_VER)
+# define PYBIND11_COMPILER_TYPE "_msvc"
+#elif defined(__INTEL_COMPILER)
+# define PYBIND11_COMPILER_TYPE "_icc"
+#elif defined(__clang__)
+# define PYBIND11_COMPILER_TYPE "_clang"
+#elif defined(__PGI)
+# define PYBIND11_COMPILER_TYPE "_pgi"
+#elif defined(__MINGW32__)
+# define PYBIND11_COMPILER_TYPE "_mingw"
+#elif defined(__CYGWIN__)
+# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
+#elif defined(__GNUC__)
+# define PYBIND11_COMPILER_TYPE "_gcc"
+#else
+# define PYBIND11_COMPILER_TYPE "_unknown"
+#endif
+
+#if defined(_LIBCPP_VERSION)
+# define PYBIND11_STDLIB "_libcpp"
+#elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
+# define PYBIND11_STDLIB "_libstdcpp"
+#else
+# define PYBIND11_STDLIB ""
+#endif
+
+/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
+#if defined(__GXX_ABI_VERSION)
+# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
+#else
+# define PYBIND11_BUILD_ABI ""
+#endif
#if defined(WITH_THREAD)
# define PYBIND11_INTERNALS_KIND ""
#endif
#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \
- PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND "__"
+ PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \
- PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND "__"
+ PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
/// Each module locally stores a pointer to the `internals` data. The data
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
return internals_pp;
}
+inline void translate_exception(std::exception_ptr p) {
+ try {
+ if (p) std::rethrow_exception(p);
+ } catch (error_already_set &e) { e.restore(); return;
+ } catch (const builtin_exception &e) { e.set_error(); return;
+ } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return;
+ } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
+ } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
+ } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
+ } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return;
+ } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
+ } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return;
+ } catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
+ return;
+ }
+}
+
+#if !defined(__GLIBCXX__)
+inline void translate_local_exception(std::exception_ptr p) {
+ try {
+ if (p) std::rethrow_exception(p);
+ } catch (error_already_set &e) { e.restore(); return;
+ } catch (const builtin_exception &e) { e.set_error(); return;
+ }
+}
+#endif
+
/// Return a reference to the current `internals` data
PYBIND11_NOINLINE inline internals &get_internals() {
auto **&internals_pp = get_internals_pp();
if (internals_pp && *internals_pp)
return **internals_pp;
+ // Ensure that the GIL is held since we will need to make Python calls.
+ // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
+ struct gil_scoped_acquire_local {
+ gil_scoped_acquire_local() : state (PyGILState_Ensure()) {}
+ ~gil_scoped_acquire_local() { PyGILState_Release(state); }
+ const PyGILState_STATE state;
+ } gil;
+
constexpr auto *id = PYBIND11_INTERNALS_ID;
auto builtins = handle(PyEval_GetBuiltins());
if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
//
// libstdc++ doesn't require this (types there are identified only by name)
#if !defined(__GLIBCXX__)
- (*internals_pp)->registered_exception_translators.push_front(
- [](std::exception_ptr p) -> void {
- try {
- if (p) std::rethrow_exception(p);
- } catch (error_already_set &e) { e.restore(); return;
- } catch (const builtin_exception &e) { e.set_error(); return;
- }
- }
- );
+ (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
#endif
} else {
if (!internals_pp) internals_pp = new internals*();
internals_ptr->istate = tstate->interp;
#endif
builtins[id] = capsule(internals_pp);
- internals_ptr->registered_exception_translators.push_front(
- [](std::exception_ptr p) -> void {
- try {
- if (p) std::rethrow_exception(p);
- } catch (error_already_set &e) { e.restore(); return;
- } catch (const builtin_exception &e) { e.set_error(); return;
- } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return;
- } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
- } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
- } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
- } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return;
- } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
- } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return;
- } catch (...) {
- PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
- return;
- }
- }
- );
+ internals_ptr->registered_exception_translators.push_front(&translate_exception);
internals_ptr->static_property_type = make_static_property_type();
internals_ptr->default_metaclass = make_default_metaclass();
internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
#include <cxxabi.h>
#endif
+#include "common.h"
+
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
NAMESPACE_BEGIN(detail)
/// Erase all occurrences of a substring
# 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
}
}
- static PYBIND11_DESCR descriptor() {
- constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
- constexpr bool show_order = is_eigen_dense_map<Type>::value;
- constexpr bool show_c_contiguous = show_order && requires_row_major;
- constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major;
-
- return type_descr(_("numpy.ndarray[") + npy_format_descriptor<Scalar>::name() +
- _("[") + _<fixed_rows>(_<(size_t) rows>(), _("m")) +
- _(", ") + _<fixed_cols>(_<(size_t) cols>(), _("n")) +
- _("]") +
- // For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to be
- // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride
- // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output
- // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to
- // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you
- // *gave* a numpy.ndarray of the right type and dimensions.
- _<show_writeable>(", flags.writeable", "") +
- _<show_c_contiguous>(", flags.c_contiguous", "") +
- _<show_f_contiguous>(", flags.f_contiguous", "") +
- _("]")
- );
- }
+ static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
+ static constexpr bool show_order = is_eigen_dense_map<Type>::value;
+ static constexpr bool show_c_contiguous = show_order && requires_row_major;
+ static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major;
+
+ static constexpr auto descriptor =
+ _("numpy.ndarray[") + npy_format_descriptor<Scalar>::name +
+ _("[") + _<fixed_rows>(_<(size_t) rows>(), _("m")) +
+ _(", ") + _<fixed_cols>(_<(size_t) cols>(), _("n")) +
+ _("]") +
+ // For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to be
+ // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride
+ // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output
+ // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to
+ // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you
+ // *gave* a numpy.ndarray of the right type and dimensions.
+ _<show_writeable>(", flags.writeable", "") +
+ _<show_c_contiguous>(", flags.c_contiguous", "") +
+ _<show_f_contiguous>(", flags.f_contiguous", "") +
+ _("]");
};
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
return cast_impl(src, policy, parent);
}
- static PYBIND11_DESCR name() { return props::descriptor(); }
+ static constexpr auto name = props::descriptor;
operator Type*() { return &value; }
operator Type&() { return value; }
}
}
- static PYBIND11_DESCR name() { return props::descriptor(); }
+ static constexpr auto name = props::descriptor;
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
// types but not bound arguments). We still provide them (with an explicitly delete) so that
}
static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); }
- static PYBIND11_DESCR name() { return props::descriptor(); }
+ static constexpr auto name = props::descriptor;
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
// types but not bound arguments). We still provide them (with an explicitly delete) so that
}
PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[")
- + npy_format_descriptor<Scalar>::name() + _("]"));
+ + npy_format_descriptor<Scalar>::name + _("]"));
};
NAMESPACE_END(detail)
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
+ `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.)
+
+ .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
\endrst */
inline void initialize_interpreter(bool init_signal_handlers = true) {
if (Py_IsInitialized())
}
}
- value = [func](Args... args) -> Return {
- gil_scoped_acquire acq;
- object retval(func(std::forward<Args>(args)...));
- /* Visual studio 2015 parser issue: need parentheses around this expression */
- return (retval.template cast<Return>());
+ // 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&) = default;
+ ~func_handle() {
+ gil_scoped_acquire acq;
+ function kill_f(std::move(f));
+ }
};
+
+ // to emulate 'move initialization capture' in C++11
+ struct func_wrapper {
+ func_handle hfunc;
+ func_wrapper(func_handle&& hf): hfunc(std::move(hf)) {}
+ Return operator()(Args... args) const {
+ gil_scoped_acquire acq;
+ object retval(hfunc.f(std::forward<Args>(args)...));
+ /* Visual studio 2015 parser issue: need parentheses around this expression */
+ return (retval.template cast<Return>());
+ }
+ };
+
+ value = func_wrapper(func_handle(std::move(func)));
return true;
}
return cpp_function(std::forward<Func>(f_), policy).release();
}
- PYBIND11_TYPE_CASTER(type, _("Callable[[") +
- argument_loader<Args...>::arg_names() + _("], ") +
- make_caster<retval_type>::name() +
- _("]"));
+ PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster<Args>::name...) + _("], ")
+ + make_caster<retval_type>::name + _("]"));
};
NAMESPACE_END(detail)
private:
using traits_type = std::streambuf::traits_type;
- char d_buffer[1024];
+ const size_t buf_size;
+ std::unique_ptr<char[]> d_buffer;
object pywrite;
object pyflush;
// This subtraction cannot be negative, so dropping the sign
str line(pbase(), static_cast<size_t>(pptr() - pbase()));
- pywrite(line);
- pyflush();
+ {
+ gil_scoped_acquire tmp;
+ pywrite(line);
+ pyflush();
+ }
setp(pbase(), epptr());
}
}
public:
- pythonbuf(object pyostream)
- : pywrite(pyostream.attr("write")),
+
+ pythonbuf(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, d_buffer + sizeof(d_buffer) - 1);
+ setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
}
+ pythonbuf(pythonbuf&&) = default;
+
/// Sync before destroy
~pythonbuf() {
sync();
return class_<detail::OstreamRedirect>(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_, args) { self_.exit(); });
}
NAMESPACE_END(PYBIND11_NAMESPACE)
#include <numeric>
#include <algorithm>
#include <array>
+#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <string>
-#include <initializer_list>
#include <functional>
#include <utility>
+#include <vector>
#include <typeindex>
#if defined(_MSC_VER)
return *ptr;
}
+template <typename T> struct same_size {
+ template <typename U> using as = bool_constant<sizeof(T) == sizeof(U)>;
+};
+
+template <typename Concrete> constexpr int platform_lookup() { return -1; }
+
+// Lookup a type according to its size, and return a value corresponding to the NumPy typenum.
+template <typename Concrete, typename T, typename... Ts, typename... Ints>
+constexpr int platform_lookup(int I, Ints... Is) {
+ return sizeof(Concrete) == sizeof(T) ? I : platform_lookup<Concrete, Ts...>(Is...);
+}
+
struct npy_api {
enum constants {
NPY_ARRAY_C_CONTIGUOUS_ = 0x0001,
NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_,
NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_,
NPY_OBJECT_ = 17,
- NPY_STRING_, NPY_UNICODE_, NPY_VOID_
+ NPY_STRING_, NPY_UNICODE_, NPY_VOID_,
+ // Platform-dependent normalization
+ NPY_INT8_ = NPY_BYTE_,
+ NPY_UINT8_ = NPY_UBYTE_,
+ NPY_INT16_ = NPY_SHORT_,
+ NPY_UINT16_ = NPY_USHORT_,
+ // `npy_common.h` defines the integer aliases. In order, it checks:
+ // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR
+ // and assigns the alias to the first matching size, so we should check in this order.
+ NPY_INT32_ = platform_lookup<std::int32_t, long, int, short>(
+ NPY_LONG_, NPY_INT_, NPY_SHORT_),
+ NPY_UINT32_ = platform_lookup<std::uint32_t, unsigned long, unsigned int, unsigned short>(
+ NPY_ULONG_, NPY_UINT_, NPY_USHORT_),
+ NPY_INT64_ = platform_lookup<std::int64_t, long, long long, int>(
+ NPY_LONG_, NPY_LONGLONG_, NPY_INT_),
+ NPY_UINT64_ = platform_lookup<std::uint64_t, unsigned long, unsigned long long, unsigned int>(
+ NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
};
typedef struct {
typedef T type;
static constexpr bool is_array = false;
static constexpr bool is_empty = false;
- static PYBIND11_DESCR extents() { return _(""); }
+ static constexpr auto extents = _("");
static void append_extents(list& /* shape */) { }
};
// Computes underlying type and a comma-separated list of extents for array
array_info<T>::append_extents(shape);
}
- template<typename T2 = T, enable_if_t<!array_info<T2>::is_array, int> = 0>
- static PYBIND11_DESCR extents() {
- return _<N>();
- }
-
- template<typename T2 = T, enable_if_t<array_info<T2>::is_array, int> = 0>
- static PYBIND11_DESCR extents() {
- return concat(_<N>(), array_info<T>::extents());
- }
+ static constexpr auto extents = _<array_info<T>::is_array>(
+ concat(_<N>(), array_info<T>::extents), _<N>()
+ );
};
// For numpy we have special handling for arrays of characters, so we don't include
// the size in the array extents.
/// 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.release().ptr(), &ptr) || !ptr)
+ if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr)
throw error_already_set();
return reinterpret_steal<dtype>(ptr);
}
// Reference to element at a given index
template<typename... Ix> const T& at(Ix... index) const {
- if (sizeof...(index) != ndim())
+ if ((ssize_t) sizeof...(index) != ndim())
fail_dim_check(sizeof...(index), "index dimension mismatch");
return *(static_cast<const T*>(array::data()) + byte_offset(ssize_t(index)...) / itemsize());
}
// Mutable reference to element at a given index
template<typename... Ix> T& mutable_at(Ix... index) {
- if (sizeof...(index) != ndim())
+ if ((ssize_t) sizeof...(index) != ndim())
fail_dim_check(sizeof...(index), "index dimension mismatch");
return *(static_cast<T*>(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize());
}
struct format_descriptor<T, detail::enable_if_t<detail::array_info<T>::is_array>> {
static std::string format() {
using namespace detail;
- PYBIND11_DESCR extents = _("(") + array_info<T>::extents() + _(")");
- return extents.text() + format_descriptor<remove_all_extents_t<T>>::format();
+ static constexpr auto extents = _("(") + array_info<T>::extents + _(")");
+ return extents.text + format_descriptor<remove_all_extents_t<T>>::format();
}
};
static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
return src.inc_ref();
}
- PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name());
+ PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
};
template <typename T>
}
};
-template <typename T> struct npy_format_descriptor<T, enable_if_t<satisfies_any_of<T, std::is_arithmetic, is_complex>::value>> {
+template <typename T, typename = void>
+struct npy_format_descriptor_name;
+
+template <typename T>
+struct npy_format_descriptor_name<T, enable_if_t<std::is_integral<T>::value>> {
+ static constexpr auto name = _<std::is_same<T, bool>::value>(
+ _("bool"), _<std::is_signed<T>::value>("int", "uint") + _<sizeof(T)*8>()
+ );
+};
+
+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>(
+ _("float") + _<sizeof(T)*8>(), _("longdouble")
+ );
+};
+
+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>(
+ _("complex") + _<sizeof(typename T::value_type)*16>(), _("longcomplex")
+ );
+};
+
+template <typename T>
+struct npy_format_descriptor<T, enable_if_t<satisfies_any_of<T, std::is_arithmetic, is_complex>::value>>
+ : npy_format_descriptor_name<T> {
private:
// NB: the order here must match the one in common.h
constexpr static const int values[15] = {
npy_api::NPY_BOOL_,
- npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_SHORT_, npy_api::NPY_USHORT_,
- npy_api::NPY_INT_, npy_api::NPY_UINT_, npy_api::NPY_LONGLONG_, npy_api::NPY_ULONGLONG_,
+ npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_INT16_, npy_api::NPY_UINT16_,
+ npy_api::NPY_INT32_, npy_api::NPY_UINT32_, npy_api::NPY_INT64_, npy_api::NPY_UINT64_,
npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_,
npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_
};
static pybind11::dtype dtype() {
if (auto ptr = npy_api::get().PyArray_DescrFromType_(value))
- return reinterpret_borrow<pybind11::dtype>(ptr);
+ return reinterpret_steal<pybind11::dtype>(ptr);
pybind11_fail("Unsupported buffer format!");
}
- template <typename T2 = T, enable_if_t<std::is_integral<T2>::value, int> = 0>
- static PYBIND11_DESCR name() {
- return _<std::is_same<T, bool>::value>(_("bool"),
- _<std::is_signed<T>::value>("int", "uint") + _<sizeof(T)*8>());
- }
- template <typename T2 = T, enable_if_t<std::is_floating_point<T2>::value, int> = 0>
- static PYBIND11_DESCR name() {
- return _<std::is_same<T, float>::value || std::is_same<T, double>::value>(
- _("float") + _<sizeof(T)*8>(), _("longdouble"));
- }
- template <typename T2 = T, enable_if_t<is_complex<T2>::value, int> = 0>
- static PYBIND11_DESCR name() {
- return _<std::is_same<typename T2::value_type, float>::value || std::is_same<typename T2::value_type, double>::value>(
- _("complex") + _<sizeof(typename T2::value_type)*16>(), _("longcomplex"));
- }
};
#define PYBIND11_DECL_CHAR_FMT \
- static PYBIND11_DESCR name() { return _("S") + _<N>(); } \
+ static constexpr auto name = _("S") + _<N>(); \
static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); }
template <size_t N> struct npy_format_descriptor<char[N]> { PYBIND11_DECL_CHAR_FMT };
template <size_t N> struct npy_format_descriptor<std::array<char, N>> { PYBIND11_DECL_CHAR_FMT };
public:
static_assert(!array_info<T>::is_empty, "Zero-sized arrays are not supported");
- static PYBIND11_DESCR name() { return _("(") + array_info<T>::extents() + _(")") + base_descr::name(); }
+ static constexpr auto name = _("(") + array_info<T>::extents + _(")") + base_descr::name;
static pybind11::dtype dtype() {
list shape;
array_info<T>::append_extents(shape);
private:
using base_descr = npy_format_descriptor<typename std::underlying_type<T>::type>;
public:
- static PYBIND11_DESCR name() { return base_descr::name(); }
+ static constexpr auto name = base_descr::name;
static pybind11::dtype dtype() { return base_descr::dtype(); }
};
};
inline PYBIND11_NOINLINE void register_structured_dtype(
- const std::initializer_list<field_descriptor>& fields,
+ any_container<field_descriptor> fields,
const std::type_info& tinfo, ssize_t itemsize,
bool (*direct_converter)(PyObject *, void *&)) {
if (numpy_internals.get_type_info(tinfo, false))
pybind11_fail("NumPy: dtype is already registered");
+ // Use ordered fields because order matters as of NumPy 1.14:
+ // https://docs.scipy.org/doc/numpy/release.html#multiple-field-indexing-assignment-of-structured-arrays
+ std::vector<field_descriptor> ordered_fields(std::move(fields));
+ std::sort(ordered_fields.begin(), ordered_fields.end(),
+ [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; });
+
list names, formats, offsets;
- for (auto field : fields) {
+ for (auto& field : ordered_fields) {
if (!field.descr)
pybind11_fail(std::string("NumPy: unsupported field dtype: `") +
field.name + "` @ " + tinfo.name());
// - https://github.com/numpy/numpy/pull/7798
// Because of this, we won't use numpy's logic to generate buffer format
// strings and will just do it ourselves.
- std::vector<field_descriptor> ordered_fields(fields);
- std::sort(ordered_fields.begin(), ordered_fields.end(),
- [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; });
ssize_t offset = 0;
std::ostringstream oss;
// mark the structure as unaligned with '^', because numpy and C++ don't
template <typename T, typename SFINAE> struct npy_format_descriptor {
static_assert(is_pod_struct<T>::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype");
- static PYBIND11_DESCR name() { return make_caster<T>::name(); }
+ static constexpr auto name = make_caster<T>::name;
static pybind11::dtype dtype() {
return reinterpret_borrow<pybind11::dtype>(dtype_ptr());
return format_str;
}
- static void register_dtype(const std::initializer_list<field_descriptor>& fields) {
- register_structured_dtype(fields, typeid(typename std::remove_cv<T>::type),
+ static void register_dtype(any_container<field_descriptor> fields) {
+ register_structured_dtype(std::move(fields), typeid(typename std::remove_cv<T>::type),
sizeof(T), &direct_converter);
}
#define PYBIND11_NUMPY_DTYPE(Type, ...) \
::pybind11::detail::npy_format_descriptor<Type>::register_dtype \
- ({PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)})
+ (::std::vector<::pybind11::detail::field_descriptor> \
+ {PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)})
#ifdef _MSC_VER
#define PYBIND11_MAP2_LIST_NEXT1(test, next) \
#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \
::pybind11::detail::npy_format_descriptor<Type>::register_dtype \
- ({PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)})
+ (::std::vector<::pybind11::detail::field_descriptor> \
+ {PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)})
#endif // __CLION_IDE__
private:
remove_reference_t<Func> f;
- template <size_t Index> using param_n_t = typename pack_element<Index, typename vectorize_arg<Args>::call_type...>::type;
+ // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag
+ // when arg_call_types is manually inlined.
+ using arg_call_types = std::tuple<typename vectorize_arg<Args>::call_type...>;
+ template <size_t Index> using param_n_t = typename std::tuple_element<Index, arg_call_types>::type;
// Runs a vectorized function given arguments tuple and three index sequences:
// - Index is the full set of 0 ... (N-1) argument indices;
if (trivial == broadcast_trivial::f_trivial) result = array_t<Return, array::f_style>(shape);
else result = array_t<Return>(shape);
- if (size == 0) return result;
+ if (size == 0) return std::move(result);
/* Call the function */
if (trivial == broadcast_trivial::non_trivial)
else
apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq);
- return result;
+ return std::move(result);
}
template <size_t... Index, size_t... VIndex, size_t... BIndex>
}
template <typename T, int Flags> struct handle_type_name<array_t<T, Flags>> {
- static PYBIND11_DESCR name() {
- return _("numpy.ndarray[") + npy_format_descriptor<T>::name() + _("]");
- }
+ static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor<T>::name + _("]");
};
NAMESPACE_END(detail)
#pragma once
-#if defined(_MSC_VER)
+#if defined(__INTEL_COMPILER)
+# pragma warning push
+# pragma warning disable 68 // integer conversion resulted in a change of sign
+# pragma warning disable 186 // pointless comparison of unsigned integer with zero
+# pragma warning disable 878 // incompatible exception specifications
+# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template
+# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
+# pragma warning disable 1786 // function "strdup" was declared deprecated
+# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard
+# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline"
+#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
# pragma warning(disable: 4702) // warning C4702: unreachable code
# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified
-#elif defined(__INTEL_COMPILER)
-# pragma warning(push)
-# pragma warning(disable: 68) // integer conversion resulted in a change of sign
-# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero
-# pragma warning(disable: 878) // incompatible exception specifications
-# pragma warning(disable: 1334) // the "template" keyword used for syntactic disambiguation may only be used within a template
-# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
-# pragma warning(disable: 1875) // offsetof applied to non-POD (Plain Old Data) types is nonstandard
-# pragma warning(disable: 2196) // warning #2196: routine is both "inline" and "noinline"
#elif defined(__GNUG__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "detail/class.h"
#include "detail/init.h"
+#if defined(__GNUG__) && !defined(__clang__)
+# include <cxxabi.h>
+#endif
+
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
class cpp_function : public function {
public:
cpp_function() { }
+ cpp_function(std::nullptr_t) { }
/// Construct a cpp_function from a vanilla function pointer
template <typename Return, typename... Args, typename... Extra>
template <typename Func, typename Return, typename... Args, typename... Extra>
void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) {
using namespace detail;
-
struct capture { remove_reference_t<Func> f; };
/* Store the function including any extra state it might have (e.g. a lambda capture object) */
process_attributes<Extra...>::init(extra..., rec);
/* Generate a readable signature describing the function's arguments and return value types */
- PYBIND11_DESCR signature = _("(") + cast_in::arg_names() + _(") -> ") + cast_out::name();
+ static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name;
+ PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types();
/* Register the function with Python from generic (non-templated) code */
- initialize_generic(rec, signature.text(), signature.types(), sizeof...(Args));
+ initialize_generic(rec, signature.text, types.data(), sizeof...(Args));
if (cast_in::has_args) rec->has_args = true;
if (cast_in::has_kwargs) rec->has_kwargs = true;
/* Generate a proper function signature */
std::string signature;
- size_t type_depth = 0, char_index = 0, type_index = 0, arg_index = 0;
- while (true) {
- char c = text[char_index++];
- if (c == '\0')
- break;
+ size_t type_index = 0, arg_index = 0;
+ for (auto *pc = text; *pc != '\0'; ++pc) {
+ const auto c = *pc;
if (c == '{') {
- // Write arg name for everything except *args, **kwargs and return type.
- if (type_depth == 0 && text[char_index] != '*' && arg_index < args) {
- if (!rec->args.empty() && rec->args[arg_index].name) {
- signature += rec->args[arg_index].name;
- } else if (arg_index == 0 && rec->is_method) {
- signature += "self";
- } else {
- signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0));
- }
- signature += ": ";
+ // Write arg name for everything except *args and **kwargs.
+ if (*(pc + 1) == '*')
+ continue;
+
+ if (arg_index < rec->args.size() && rec->args[arg_index].name) {
+ signature += rec->args[arg_index].name;
+ } else if (arg_index == 0 && rec->is_method) {
+ signature += "self";
+ } else {
+ signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0));
}
- ++type_depth;
+ signature += ": ";
} else if (c == '}') {
- --type_depth;
- if (type_depth == 0) {
- if (arg_index < rec->args.size() && rec->args[arg_index].descr) {
- signature += "=";
- signature += rec->args[arg_index].descr;
- }
- arg_index++;
+ // Write default value if available.
+ if (arg_index < rec->args.size() && rec->args[arg_index].descr) {
+ signature += " = ";
+ signature += rec->args[arg_index].descr;
}
+ arg_index++;
} else if (c == '%') {
const std::type_info *t = types[type_index++];
if (!t)
signature += c;
}
}
- if (type_depth != 0 || types[type_index] != nullptr)
+ if (arg_index != args || types[type_index] != nullptr)
pybind11_fail("Internal error while parsing type signature (2)");
- #if !defined(PYBIND11_CONSTEXPR_DESCR)
- delete[] types;
- delete[] text;
- #endif
-
#if PY_MAJOR_VERSION < 3
if (strcmp(rec->name, "__next__") == 0) {
std::free(rec->name);
using namespace detail;
/* Iterator over the list of potentially admissible overloads */
- function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr),
- *it = overloads;
+ const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr),
+ *it = overloads;
/* Need to know how many arguments + keyword arguments there are to pick the right overload */
const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in);
result other than PYBIND11_TRY_NEXT_OVERLOAD.
*/
- function_record &func = *it;
+ const function_record &func = *it;
size_t pos_args = func.nargs; // Number of positional arguments that we need
if (func.has_args) --pos_args; // (but don't count py::args
if (func.has_kwargs) --pos_args; // or py::kwargs)
function_call call(func, parent);
- size_t args_to_copy = std::min(pos_args, n_args_in);
+ size_t args_to_copy = (std::min)(pos_args, n_args_in); // Protect std::min with parentheses
size_t args_copied = 0;
// 0. Inject new-style `self` argument
// 1. Copy any position arguments given.
bool bad_arg = false;
for (; args_copied < args_to_copy; ++args_copied) {
- argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr;
+ const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr;
if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) {
bad_arg = true;
break;
result = PYBIND11_TRY_NEXT_OVERLOAD;
}
- if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD)
+ if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) {
+ // The error reporting logic below expects 'it' to be valid, as it would be
+ // if we'd encountered this failure in the first-pass loop.
+ if (!result)
+ it = &call.func;
break;
+ }
}
}
} catch (error_already_set &e) {
e.restore();
return nullptr;
+#if defined(__GNUG__) && !defined(__clang__)
+ } catch ( abi::__forced_unwind& ) {
+ throw;
+#endif
} catch (...) {
/* When an exception is caught, give each registered exception
translator a chance to translate it to a Python exception
" arguments. The following argument types are supported:\n";
int ctr = 0;
- for (function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) {
+ for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) {
msg += " "+ std::to_string(++ctr) + ". ";
bool wrote_sig = false;
tinfo->type = (PyTypeObject *) m_ptr;
tinfo->cpptype = rec.type;
tinfo->type_size = rec.type_size;
+ tinfo->type_align = rec.type_align;
tinfo->operator_new = rec.operator_new;
tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size);
tinfo->init_instance = rec.init_instance;
tinfo->get_buffer_data = get_buffer_data;
}
+ // rec_func must be set for either fget or fset.
void def_property_static_impl(const char *name,
handle fget, handle fset,
- detail::function_record *rec_fget) {
- const auto is_static = !(rec_fget->is_method && rec_fget->scope);
- const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings();
-
+ detail::function_record *rec_func) {
+ const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope);
+ const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings();
auto property = handle((PyObject *) (is_static ? get_internals().static_property_type
: &PyProperty_Type));
attr(name) = property(fget.ptr() ? fget : none(),
fset.ptr() ? fset : none(),
/*deleter*/none(),
- pybind11::str(has_doc ? rec_fget->doc : ""));
+ pybind11::str(has_doc ? rec_func->doc : ""));
}
};
: std::true_type { };
/// Call class-specific delete if it exists or global otherwise. Can also be an overload set.
template <typename T, enable_if_t<has_operator_delete<T>::value, int> = 0>
-void call_operator_delete(T *p, size_t) { T::operator delete(p); }
+void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); }
template <typename T, enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int> = 0>
-void call_operator_delete(T *p, size_t s) { T::operator delete(p, s); }
+void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); }
-inline void call_operator_delete(void *p, size_t) { ::operator delete(p); }
+inline void call_operator_delete(void *p, size_t s, size_t a) {
+ (void)s; (void)a;
+#if defined(PYBIND11_CPP17)
+ if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ ::operator delete(p, s, std::align_val_t(a));
+ else
+ ::operator delete(p, s);
+#else
+ ::operator delete(p);
+#endif
+}
NAMESPACE_END(detail)
auto method_adaptor(F &&f) -> decltype(std::forward<F>(f)) { return std::forward<F>(f); }
template <typename Derived, typename Return, typename Class, typename... Args>
-auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { return pmf; }
+auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) {
+ static_assert(detail::is_accessible_base_of<Class, Derived>::value,
+ "Cannot bind an inaccessible base class method; use a lambda definition instead");
+ return pmf;
+}
template <typename Derived, typename Return, typename Class, typename... Args>
-auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { return pmf; }
+auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const {
+ static_assert(detail::is_accessible_base_of<Class, Derived>::value,
+ "Cannot bind an inaccessible base class method; use a lambda definition instead");
+ return pmf;
+}
template <typename type_, typename... options>
class class_ : public detail::generic_type {
record.name = name;
record.type = &typeid(type);
record.type_size = sizeof(conditional_t<has_alias, type_alias, type>);
+ record.type_align = alignof(conditional_t<has_alias, type_alias, type>&);
record.holder_size = sizeof(holder_type);
record.init_instance = init_instance;
record.dealloc = dealloc;
- record.default_holder = std::is_same<holder_type, std::unique_ptr<type>>::value;
+ record.default_holder = detail::is_instantiation<std::unique_ptr, holder_type>::value;
set_operator_new<type>(&record);
"def_static(...) called with a non-static member function pointer");
cpp_function cf(std::forward<Func>(f), name(name_), scope(*this),
sibling(getattr(*this, name_, none())), extra...);
- attr(cf.name()) = cf;
+ attr(cf.name()) = staticmethod(cf);
return *this;
}
template <typename C, typename D, typename... Extra>
class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) {
- static_assert(std::is_base_of<C, type>::value, "def_readwrite() requires a class member (or base class member)");
+ static_assert(std::is_same<C, type>::value || std::is_base_of<C, type>::value, "def_readwrite() requires a class member (or base class member)");
cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)),
fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this));
def_property(name, fget, fset, return_value_policy::reference_internal, extra...);
template <typename C, typename D, typename... Extra>
class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) {
- static_assert(std::is_base_of<C, type>::value, "def_readonly() requires a class member (or base class member)");
+ static_assert(std::is_same<C, type>::value || std::is_base_of<C, type>::value, "def_readonly() requires a class member (or base class member)");
cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this));
def_property_readonly(name, fget, return_value_policy::reference_internal, extra...);
return *this;
/// Uses cpp_function's return_value_policy by default
template <typename... Extra>
class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) {
- return def_property(name, fget, cpp_function(), extra...);
+ return def_property(name, fget, nullptr, extra...);
}
/// Uses return_value_policy::reference by default
/// Uses cpp_function's return_value_policy by default
template <typename... Extra>
class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) {
- return def_property_static(name, fget, cpp_function(), extra...);
+ return def_property_static(name, fget, nullptr, extra...);
}
/// Uses return_value_policy::reference_internal by default
/// Uses cpp_function's return_value_policy by default
template <typename... Extra>
class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) {
+ static_assert( 0 == detail::constexpr_sum(std::is_base_of<arg, Extra>::value...),
+ "Argument annotations are not allowed for properties");
auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset);
- char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
- detail::process_attributes<Extra...>::init(extra..., rec_fget);
- if (rec_fget->doc && rec_fget->doc != doc_prev) {
- free(doc_prev);
- rec_fget->doc = strdup(rec_fget->doc);
+ auto *rec_active = rec_fget;
+ if (rec_fget) {
+ char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
+ detail::process_attributes<Extra...>::init(extra..., rec_fget);
+ if (rec_fget->doc && rec_fget->doc != doc_prev) {
+ free(doc_prev);
+ rec_fget->doc = strdup(rec_fget->doc);
+ }
}
if (rec_fset) {
- doc_prev = rec_fset->doc;
+ char *doc_prev = rec_fset->doc;
detail::process_attributes<Extra...>::init(extra..., rec_fset);
if (rec_fset->doc && rec_fset->doc != doc_prev) {
free(doc_prev);
rec_fset->doc = strdup(rec_fset->doc);
}
+ if (! rec_active) rec_active = rec_fset;
}
- def_property_static_impl(name, fget, fset, rec_fget);
+ def_property_static_impl(name, fget, fset, rec_active);
return *this;
}
v_h.set_holder_constructed(false);
}
else {
- detail::call_operator_delete(v_h.value_ptr<type>(), v_h.type->type_size);
+ detail::call_operator_delete(v_h.value_ptr<type>(),
+ v_h.type->type_size,
+ v_h.type->type_align
+ );
}
v_h.value_ptr() = nullptr;
}
return {std::forward<GetState>(g), std::forward<SetState>(s)};
}
+NAMESPACE_BEGIN(detail)
+struct enum_base {
+ enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { }
+
+ PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) {
+ m_base.attr("__entries") = dict();
+ auto property = handle((PyObject *) &PyProperty_Type);
+ auto static_property = handle((PyObject *) get_internals().static_property_type);
+
+ m_base.attr("__repr__") = cpp_function(
+ [](handle arg) -> str {
+ handle type = arg.get_type();
+ object type_name = type.attr("__name__");
+ dict entries = type.attr("__entries");
+ for (const auto &kv : entries) {
+ object other = kv.second[int_(0)];
+ if (other.equal(arg))
+ return pybind11::str("{}.{}").format(type_name, kv.first);
+ }
+ return pybind11::str("{}.???").format(type_name);
+ }, is_method(m_base)
+ );
+
+ m_base.attr("name") = property(cpp_function(
+ [](handle arg) -> str {
+ dict entries = arg.get_type().attr("__entries");
+ for (const auto &kv : entries) {
+ if (handle(kv.second[int_(0)]).equal(arg))
+ return pybind11::str(kv.first);
+ }
+ return "???";
+ }, is_method(m_base)
+ ));
+
+ m_base.attr("__doc__") = static_property(cpp_function(
+ [](handle arg) -> std::string {
+ std::string docstring;
+ dict entries = arg.attr("__entries");
+ if (((PyTypeObject *) arg.ptr())->tp_doc)
+ docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n";
+ docstring += "Members:";
+ for (const auto &kv : entries) {
+ auto key = std::string(pybind11::str(kv.first));
+ auto comment = kv.second[int_(1)];
+ docstring += "\n\n " + key;
+ if (!comment.is_none())
+ docstring += " : " + (std::string) pybind11::str(comment);
+ }
+ return docstring;
+ }
+ ), none(), none(), "");
+
+ m_base.attr("__members__") = static_property(cpp_function(
+ [](handle arg) -> dict {
+ dict entries = arg.attr("__entries"), m;
+ for (const auto &kv : entries)
+ m[kv.first] = kv.second[int_(0)];
+ return m;
+ }), none(), none(), ""
+ );
+
+ #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \
+ m_base.attr(op) = cpp_function( \
+ [](object a, object b) { \
+ if (!a.get_type().is(b.get_type())) \
+ strict_behavior; \
+ return expr; \
+ }, \
+ is_method(m_base))
+
+ #define PYBIND11_ENUM_OP_CONV(op, expr) \
+ m_base.attr(op) = cpp_function( \
+ [](object a_, object b_) { \
+ int_ a(a_), b(b_); \
+ return expr; \
+ }, \
+ is_method(m_base))
+
+ #define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \
+ m_base.attr(op) = cpp_function( \
+ [](object a_, object b) { \
+ int_ a(a_); \
+ return expr; \
+ }, \
+ is_method(m_base))
+
+ if (is_convertible) {
+ PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b));
+ PYBIND11_ENUM_OP_CONV_LHS("__ne__", b.is_none() || !a.equal(b));
+
+ if (is_arithmetic) {
+ PYBIND11_ENUM_OP_CONV("__lt__", a < b);
+ PYBIND11_ENUM_OP_CONV("__gt__", a > b);
+ PYBIND11_ENUM_OP_CONV("__le__", a <= b);
+ PYBIND11_ENUM_OP_CONV("__ge__", a >= b);
+ PYBIND11_ENUM_OP_CONV("__and__", a & b);
+ PYBIND11_ENUM_OP_CONV("__rand__", a & b);
+ PYBIND11_ENUM_OP_CONV("__or__", a | b);
+ PYBIND11_ENUM_OP_CONV("__ror__", a | b);
+ PYBIND11_ENUM_OP_CONV("__xor__", a ^ b);
+ PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b);
+ m_base.attr("__invert__") = cpp_function(
+ [](object arg) { return ~(int_(arg)); }, is_method(m_base));
+ }
+ } else {
+ PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false);
+ PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true);
+
+ if (is_arithmetic) {
+ #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!");
+ PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW);
+ PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW);
+ PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW);
+ PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW);
+ #undef PYBIND11_THROW
+ }
+ }
+
+ #undef PYBIND11_ENUM_OP_CONV_LHS
+ #undef PYBIND11_ENUM_OP_CONV
+ #undef PYBIND11_ENUM_OP_STRICT
+
+ object getstate = cpp_function(
+ [](object arg) { return int_(arg); }, is_method(m_base));
+
+ m_base.attr("__getstate__") = getstate;
+ m_base.attr("__hash__") = getstate;
+ }
+
+ PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) {
+ dict entries = m_base.attr("__entries");
+ str name(name_);
+ if (entries.contains(name)) {
+ std::string type_name = (std::string) str(m_base.attr("__name__"));
+ throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!");
+ }
+
+ entries[name] = std::make_pair(value, doc);
+ m_base.attr(name) = value;
+ }
+
+ PYBIND11_NOINLINE void export_values() {
+ dict entries = m_base.attr("__entries");
+ for (const auto &kv : entries)
+ m_parent.attr(kv.first) = kv.second[int_(0)];
+ }
+
+ handle m_base;
+ handle m_parent;
+};
+
+NAMESPACE_END(detail)
+
/// Binds C++ enumerations and enumeration classes to Python
template <typename Type> class enum_ : public class_<Type> {
public:
- using class_<Type>::def;
- using class_<Type>::def_property_readonly_static;
+ using Base = class_<Type>;
+ using Base::def;
+ using Base::attr;
+ using Base::def_property_readonly;
+ using Base::def_property_readonly_static;
using Scalar = typename std::underlying_type<Type>::type;
template <typename... Extra>
enum_(const handle &scope, const char *name, const Extra&... extra)
- : class_<Type>(scope, name, extra...), m_entries(), m_parent(scope) {
-
+ : class_<Type>(scope, name, extra...), m_base(*this, scope) {
constexpr bool is_arithmetic = detail::any_of<std::is_same<arithmetic, Extra>...>::value;
+ constexpr bool is_convertible = std::is_convertible<Type, Scalar>::value;
+ m_base.init(is_arithmetic, is_convertible);
- auto m_entries_ptr = m_entries.inc_ref().ptr();
- def("__repr__", [name, m_entries_ptr](Type value) -> pybind11::str {
- for (const auto &kv : reinterpret_borrow<dict>(m_entries_ptr)) {
- if (pybind11::cast<Type>(kv.second) == value)
- return pybind11::str("{}.{}").format(name, kv.first);
- }
- return pybind11::str("{}.???").format(name);
- });
- def_property_readonly_static("__members__", [m_entries_ptr](object /* self */) {
- dict m;
- for (const auto &kv : reinterpret_borrow<dict>(m_entries_ptr))
- m[kv.first] = kv.second;
- return m;
- }, return_value_policy::copy);
def(init([](Scalar i) { return static_cast<Type>(i); }));
def("__int__", [](Type value) { return (Scalar) value; });
#if PY_MAJOR_VERSION < 3
def("__long__", [](Type value) { return (Scalar) value; });
#endif
- def("__eq__", [](const Type &value, Type *value2) { return value2 && value == *value2; });
- def("__ne__", [](const Type &value, Type *value2) { return !value2 || value != *value2; });
- if (is_arithmetic) {
- def("__lt__", [](const Type &value, Type *value2) { return value2 && value < *value2; });
- def("__gt__", [](const Type &value, Type *value2) { return value2 && value > *value2; });
- def("__le__", [](const Type &value, Type *value2) { return value2 && value <= *value2; });
- def("__ge__", [](const Type &value, Type *value2) { return value2 && value >= *value2; });
- }
- if (std::is_convertible<Type, Scalar>::value) {
- // Don't provide comparison with the underlying type if the enum isn't convertible,
- // i.e. if Type is a scoped enum, mirroring the C++ behaviour. (NB: we explicitly
- // convert Type to Scalar below anyway because this needs to compile).
- def("__eq__", [](const Type &value, Scalar value2) { return (Scalar) value == value2; });
- def("__ne__", [](const Type &value, Scalar value2) { return (Scalar) value != value2; });
- if (is_arithmetic) {
- def("__lt__", [](const Type &value, Scalar value2) { return (Scalar) value < value2; });
- def("__gt__", [](const Type &value, Scalar value2) { return (Scalar) value > value2; });
- def("__le__", [](const Type &value, Scalar value2) { return (Scalar) value <= value2; });
- def("__ge__", [](const Type &value, Scalar value2) { return (Scalar) value >= value2; });
- def("__invert__", [](const Type &value) { return ~((Scalar) value); });
- def("__and__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; });
- def("__or__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; });
- def("__xor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; });
- def("__rand__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; });
- def("__ror__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; });
- def("__rxor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; });
- def("__and__", [](const Type &value, const Type &value2) { return (Scalar) value & (Scalar) value2; });
- def("__or__", [](const Type &value, const Type &value2) { return (Scalar) value | (Scalar) value2; });
- def("__xor__", [](const Type &value, const Type &value2) { return (Scalar) value ^ (Scalar) value2; });
- }
- }
- def("__hash__", [](const Type &value) { return (Scalar) value; });
- // Pickling and unpickling -- needed for use with the 'multiprocessing' module
- def(pickle([](const Type &value) { return pybind11::make_tuple((Scalar) value); },
- [](tuple t) { return static_cast<Type>(t[0].cast<Scalar>()); }));
+ #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 8)
+ def("__index__", [](Type value) { return (Scalar) value; });
+ #endif
+
+ cpp_function setstate(
+ [](Type &value, Scalar arg) { value = static_cast<Type>(arg); },
+ is_method(*this));
+ attr("__setstate__") = setstate;
}
/// Export enumeration entries into the parent scope
enum_& export_values() {
- for (const auto &kv : m_entries)
- m_parent.attr(kv.first) = kv.second;
+ m_base.export_values();
return *this;
}
/// Add an enumeration entry
- enum_& value(char const* name, Type value) {
- auto v = pybind11::cast(value, return_value_policy::copy);
- this->attr(name) = v;
- m_entries[pybind11::str(name)] = v;
+ enum_& value(char const* name, Type value, const char *doc = nullptr) {
+ m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc);
return *this;
}
private:
- dict m_entries;
- handle m_parent;
+ detail::enum_base m_base;
};
NAMESPACE_BEGIN(detail)
auto const &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)
#endif
error_already_set::~error_already_set() {
- if (type) {
- error_scope scope;
+ if (m_type) {
gil_scoped_acquire gil;
- type.release().dec_ref();
- value.release().dec_ref();
- trace.release().dec_ref();
+ error_scope scope;
+ m_type.release().dec_ref();
+ m_value.release().dec_ref();
+ m_trace.release().dec_ref();
}
}
return overload;
}
+/** \rst
+ Try to retrieve a python method by the provided name from the instance pointed to by the this_ptr.
+
+ :this_ptr: The pointer to the object the overload should be retrieved for. This should be the first
+ non-trampoline class encountered in the inheritance chain.
+ :name: The name of the overloaded Python method to retrieve.
+ :return: The Python method by this name from the object or an empty function wrapper.
+ \endrst */
template <class T> function get_overload(const T *this_ptr, const char *name) {
auto tinfo = detail::get_type_info(typeid(T));
return tinfo ? get_type_overload(this_ptr, tinfo, name) : function();
} \
}
+/** \rst
+ Macro to populate the virtual method in the trampoline class. This macro tries to look up a method named 'fn'
+ from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return
+ the appropriate type. See :ref:`overriding_virtuals` for more information. This macro should be used when the method
+ name in C is not the same as the method name in Python. For example with `__str__`.
+
+ .. code-block:: cpp
+
+ std::string toString() override {
+ PYBIND11_OVERLOAD_NAME(
+ std::string, // Return type (ret_type)
+ Animal, // Parent class (cname)
+ toString, // Name of function in C++ (name)
+ "__str__", // Name of method in Python (fn)
+ );
+ }
+\endrst */
#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \
- PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \
+ PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \
return cname::fn(__VA_ARGS__)
+/** \rst
+ Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD_NAME`, except that it
+ throws if no overload can be found.
+\endrst */
#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \
- PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \
- pybind11::pybind11_fail("Tried to call pure virtual function \"" #cname "::" name "\"");
-
+ PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \
+ pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\"");
+
+/** \rst
+ Macro to populate the virtual method in the trampoline class. This macro tries to look up the method
+ from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return
+ the appropriate type. This macro should be used if the method name in C and in Python are identical.
+ See :ref:`overriding_virtuals` for more information.
+
+ .. code-block:: cpp
+
+ class PyAnimal : public Animal {
+ public:
+ // Inherit the constructors
+ using Animal::Animal;
+
+ // Trampoline (need one for each virtual function)
+ std::string go(int n_times) override {
+ PYBIND11_OVERLOAD_PURE(
+ std::string, // Return type (ret_type)
+ Animal, // Parent class (cname)
+ go, // Name of function in C++ (must match Python name) (fn)
+ n_times // Argument(s) (...)
+ );
+ }
+ };
+\endrst */
#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \
- PYBIND11_OVERLOAD_NAME(ret_type, cname, #fn, fn, __VA_ARGS__)
+ PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
+/** \rst
+ Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD`, except that it throws
+ if no overload can be found.
+\endrst */
#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \
- PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, #fn, fn, __VA_ARGS__)
+ PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)
NAMESPACE_END(PYBIND11_NAMESPACE)
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma warning(pop)
-#elif defined(__INTEL_COMPILER)
-/* Leave ignored warnings on */
#elif defined(__GNUG__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); }
/// Equivalent to ``obj is None`` in Python.
bool is_none() const { return derived().ptr() == Py_None; }
+ /// Equivalent to obj == other in Python
+ bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); }
+ bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); }
+ bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); }
+ bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); }
+ bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); }
+ bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); }
+
+ object operator-() const;
+ object operator~() const;
+ object operator+(object_api const &other) const;
+ object operator+=(object_api const &other) const;
+ object operator-(object_api const &other) const;
+ object operator-=(object_api const &other) const;
+ object operator*(object_api const &other) const;
+ object operator*=(object_api const &other) const;
+ object operator/(object_api const &other) const;
+ object operator/=(object_api const &other) const;
+ object operator|(object_api const &other) const;
+ object operator|=(object_api const &other) const;
+ object operator&(object_api const &other) const;
+ object operator&=(object_api const &other) const;
+ object operator^(object_api const &other) const;
+ object operator^=(object_api const &other) const;
+ object operator<<(object_api const &other) const;
+ object operator<<=(object_api const &other) const;
+ object operator>>(object_api const &other) const;
+ object operator>>=(object_api const &other) const;
+
PYBIND11_DEPRECATED("Use py::str(obj) instead")
pybind11::str str() const;
int ref_count() const { return static_cast<int>(Py_REFCNT(derived().ptr())); }
/// Return a handle to the Python type object underlying the instance
handle get_type() const;
+
+private:
+ bool rich_compare(object_api const &other, int value) const;
};
NAMESPACE_END(detail)
/// Constructs a new exception from the current Python error indicator, if any. The current
/// Python error indicator will be cleared.
error_already_set() : std::runtime_error(detail::error_string()) {
- PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr());
+ PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr());
}
+ error_already_set(const error_already_set &) = default;
+ error_already_set(error_already_set &&) = default;
+
inline ~error_already_set();
/// Give the currently-held error back to Python, if any. If there is currently a Python error
/// already set it is cleared first. After this call, the current object no longer stores the
/// error variables (but the `.what()` string is still available).
- void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); }
+ void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); }
// Does nothing; provided for backwards compatibility.
PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated")
/// 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 ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); }
+ bool matches(handle exc) const { return PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()); }
+
+ const object& type() const { return m_type; }
+ const object& value() const { return m_value; }
+ const object& trace() const { return m_trace; }
private:
- object type, value, trace;
+ object m_type, m_value, m_trace;
};
/** \defgroup python_builtins _
return PyObject_HasAttrString(obj.ptr(), name) == 1;
}
+inline void delattr(handle obj, handle name) {
+ if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); }
+}
+
+inline void delattr(handle obj, const char *name) {
+ if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); }
+}
+
inline object getattr(handle obj, handle name) {
PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr());
if (!result) { throw error_already_set(); }
// Match a PyObject*, which we want to convert directly to handle via its converting constructor
inline handle object_or_cast(PyObject *ptr) { return ptr; }
-
template <typename Policy>
class accessor : public object_api<accessor<Policy>> {
using key_type = typename Policy::key_type;
private:
handle obj;
- PyObject *key, *value;
+ PyObject *key = nullptr, *value = nullptr;
ssize_t pos = -1;
};
NAMESPACE_END(iterator_policies)
}
inline bool PyNone_Check(PyObject *o) { return o == Py_None; }
+#if PY_MAJOR_VERSION >= 3
+inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; }
+#endif
inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); }
+inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; }
+
class kwargs_proxy : public handle {
public:
explicit kwargs_proxy(handle h) : handle(h) { }
none() : object(Py_None, borrowed_t{}) { }
};
+#if PY_MAJOR_VERSION >= 3
+class ellipsis : public object {
+public:
+ PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check)
+ ellipsis() : object(Py_Ellipsis, borrowed_t{}) { }
+};
+#endif
+
class bool_ : public object {
public:
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool)
(ssize_t *) stop, (ssize_t *) step,
(ssize_t *) slicelength) == 0;
}
+ bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step,
+ ssize_t *slicelength) const {
+ return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr,
+ length, start,
+ stop, step,
+ slicelength) == 0;
+ }
};
class capsule : public object {
if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
}
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
+ bool empty() const { return size() == 0; }
detail::tuple_accessor operator[](size_t index) const { return {*this, index}; }
+ detail::item_accessor operator[](handle h) const { return object::operator[](h); }
detail::tuple_iterator begin() const { return {*this, 0}; }
detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; }
};
explicit dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { }
size_t size() const { return (size_t) PyDict_Size(m_ptr); }
+ 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()); }
- bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; }
- bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; }
+ template <typename T> bool contains(T &&key) const {
+ return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
+ }
private:
/// Call the `dict` Python type -- always returns a new reference
public:
PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check)
size_t size() const { return (size_t) PySequence_Size(m_ptr); }
+ bool empty() const { return size() == 0; }
detail::sequence_accessor operator[](size_t index) const { return {*this, index}; }
+ detail::item_accessor operator[](handle h) const { return object::operator[](h); }
detail::sequence_iterator begin() const { return {*this, 0}; }
detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; }
};
if (!m_ptr) pybind11_fail("Could not allocate list object!");
}
size_t size() const { return (size_t) PyList_Size(m_ptr); }
+ bool empty() const { return size() == 0; }
detail::list_accessor operator[](size_t index) const { return {*this, index}; }
+ 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 {
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());
+ }
};
class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) };
if (!m_ptr) pybind11_fail("Could not allocate set object!");
}
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 {
return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
}
void clear() 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;
+ }
};
class function : public object {
bool is_cpp_function() const { return (bool) cpp_function(); }
};
+class staticmethod : public object {
+public:
+ PYBIND11_OBJECT_CVT(staticmethod, object, detail::PyStaticMethod_Check, PyStaticMethod_New)
+};
+
class buffer : public object {
public:
PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer)
- buffer_info request(bool writable = false) {
+ buffer_info request(bool writable = false) const {
int flags = PyBUF_STRIDES | PyBUF_FORMAT;
if (writable) flags |= PyBUF_WRITABLE;
Py_buffer *view = new Py_buffer();
return (size_t) result;
}
+inline size_t len_hint(handle h) {
+#if PY_VERSION_HEX >= 0x03040000
+ ssize_t result = PyObject_LengthHint(h.ptr(), 0);
+#else
+ ssize_t result = PyObject_Length(h.ptr());
+#endif
+ if (result < 0) {
+ // Sometimes a length can't be determined at all (eg generators)
+ // In which case simply return 0
+ PyErr_Clear();
+ return 0;
+ }
+ return (size_t) result;
+}
+
inline str repr(handle h) {
PyObject *str_value = PyObject_Repr(h.ptr());
if (!str_value) throw error_already_set();
template <typename D>
handle object_api<D>::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); }
+template <typename D>
+bool object_api<D>::rich_compare(object_api const &other, int value) const {
+ int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value);
+ if (rv == -1)
+ throw error_already_set();
+ return rv == 1;
+}
+
+#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \
+ template <typename D> object object_api<D>::op() const { \
+ object result = reinterpret_steal<object>(fn(derived().ptr())); \
+ if (!result.ptr()) \
+ throw error_already_set(); \
+ return result; \
+ }
+
+#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \
+ template <typename D> \
+ object object_api<D>::op(object_api const &other) const { \
+ object result = reinterpret_steal<object>( \
+ fn(derived().ptr(), other.derived().ptr())); \
+ if (!result.ptr()) \
+ throw error_already_set(); \
+ return result; \
+ }
+
+PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert)
+PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative)
+PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add)
+PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd)
+PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract)
+PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract)
+PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply)
+PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply)
+PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide)
+PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide)
+PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or)
+PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr)
+PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And)
+PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd)
+PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor)
+PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor)
+PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift)
+PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift)
+PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift)
+PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift)
+
+#undef PYBIND11_MATH_OPERATOR_UNARY
+#undef PYBIND11_MATH_OPERATOR_BINARY
+
NAMESPACE_END(detail)
NAMESPACE_END(PYBIND11_NAMESPACE)
#include <unordered_map>
#include <iostream>
#include <list>
+#include <deque>
#include <valarray>
#if defined(_MSC_VER)
template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) {
- policy = return_value_policy_override<Key>::policy(policy);
+ if (!std::is_lvalue_reference<T>::value)
+ policy = return_value_policy_override<Key>::policy(policy);
pybind11::set s;
for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(key_conv::cast(forward_like<T>(value), policy, parent));
return s.release();
}
- PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name() + _("]"));
+ PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name + _("]"));
};
template <typename Type, typename Key, typename Value> struct map_caster {
template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) {
dict d;
- return_value_policy policy_key = return_value_policy_override<Key>::policy(policy);
- return_value_policy policy_value = return_value_policy_override<Value>::policy(policy);
+ return_value_policy policy_key = policy;
+ return_value_policy policy_value = policy;
+ if (!std::is_lvalue_reference<T>::value) {
+ policy_key = return_value_policy_override<Key>::policy(policy_key);
+ policy_value = return_value_policy_override<Value>::policy(policy_value);
+ }
for (auto &&kv : src) {
auto key = reinterpret_steal<object>(key_conv::cast(forward_like<T>(kv.first), policy_key, parent));
auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy_value, parent));
return d.release();
}
- PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name() + _(", ") + value_conv::name() + _("]"));
+ PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name + _(", ") + value_conv::name + _("]"));
};
template <typename Type, typename Value> struct list_caster {
using value_conv = make_caster<Value>;
bool load(handle src, bool convert) {
- if (!isinstance<sequence>(src))
+ if (!isinstance<sequence>(src) || isinstance<str>(src))
return false;
auto s = reinterpret_borrow<sequence>(src);
value.clear();
public:
template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) {
- policy = return_value_policy_override<Value>::policy(policy);
+ if (!std::is_lvalue_reference<T>::value)
+ policy = return_value_policy_override<Value>::policy(policy);
list l(src.size());
size_t index = 0;
for (auto &&value : src) {
return l.release();
}
- PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name() + _("]"));
+ PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name + _("]"));
};
template <typename Type, typename Alloc> struct type_caster<std::vector<Type, Alloc>>
: list_caster<std::vector<Type, Alloc>, Type> { };
+template <typename Type, typename Alloc> struct type_caster<std::deque<Type, Alloc>>
+ : list_caster<std::deque<Type, Alloc>, Type> { };
+
template <typename Type, typename Alloc> struct type_caster<std::list<Type, Alloc>>
: list_caster<std::list<Type, Alloc>, Type> { };
public:
bool load(handle src, bool convert) {
- if (!isinstance<list>(src))
+ if (!isinstance<sequence>(src))
return false;
- auto l = reinterpret_borrow<list>(src);
+ auto l = reinterpret_borrow<sequence>(src);
if (!require_size(l.size()))
return false;
size_t ctr = 0;
return l.release();
}
- PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name() + _<Resizable>(_(""), _("[") + _<Size>() + _("]")) + _("]"));
+ PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name + _<Resizable>(_(""), _("[") + _<Size>() + _("]")) + _("]"));
};
template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>>
return true;
}
- PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name() + _("]"));
+ PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]"));
};
#if PYBIND11_HAS_OPTIONAL
}
using Type = V<Ts...>;
- PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster<Ts>::name()...) + _("]"));
+ PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster<Ts>::name...) + _("]"));
};
#if PYBIND11_HAS_VARIANT
using SizeType = typename Vector::size_type;
using DiffType = typename Vector::difference_type;
+ auto wrap_i = [](DiffType i, SizeType n) {
+ if (i < 0)
+ i += n;
+ if (i < 0 || (SizeType)i >= n)
+ throw index_error();
+ return i;
+ };
+
cl.def("append",
[](Vector &v, const T &value) { v.push_back(value); },
arg("x"),
cl.def(init([](iterable it) {
auto v = std::unique_ptr<Vector>(new Vector());
- v->reserve(len(it));
+ v->reserve(len_hint(it));
for (handle h : it)
v->push_back(h.cast<T>());
return v.release();
"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("insert",
- [](Vector &v, SizeType i, const T &x) {
- if (i > v.size())
+ [](Vector &v, DiffType i, const T &x) {
+ // Can't use wrap_i; i == v.size() is OK
+ if (i < 0)
+ i += v.size();
+ if (i < 0 || (SizeType)i > v.size())
throw index_error();
- v.insert(v.begin() + (DiffType) i, x);
+ v.insert(v.begin() + i, x);
},
arg("i") , arg("x"),
"Insert an item at a given position."
);
cl.def("pop",
- [](Vector &v, SizeType i) {
- if (i >= v.size())
- throw index_error();
- T t = v[i];
- v.erase(v.begin() + (DiffType) i);
+ [wrap_i](Vector &v, DiffType i) {
+ i = wrap_i(i, v.size());
+ T t = v[(SizeType) i];
+ v.erase(v.begin() + i);
return t;
},
arg("i"),
);
cl.def("__setitem__",
- [](Vector &v, SizeType i, const T &t) {
- if (i >= v.size())
- throw index_error();
- v[i] = t;
+ [wrap_i](Vector &v, DiffType i, const T &t) {
+ i = wrap_i(i, v.size());
+ v[(SizeType)i] = t;
}
);
);
cl.def("__delitem__",
- [](Vector &v, SizeType i) {
- if (i >= v.size())
- throw index_error();
- v.erase(v.begin() + DiffType(i));
+ [wrap_i](Vector &v, DiffType i) {
+ i = wrap_i(i, v.size());
+ v.erase(v.begin() + i);
},
"Delete the list elements at index ``i``"
);
void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl) {
using T = typename Vector::value_type;
using SizeType = typename Vector::size_type;
+ using DiffType = typename Vector::difference_type;
using ItType = typename Vector::iterator;
+ auto wrap_i = [](DiffType i, SizeType n) {
+ if (i < 0)
+ i += n;
+ if (i < 0 || (SizeType)i >= n)
+ throw index_error();
+ return i;
+ };
+
cl.def("__getitem__",
- [](Vector &v, SizeType i) -> T & {
- if (i >= v.size())
- throw index_error();
- return v[i];
+ [wrap_i](Vector &v, DiffType i) -> T & {
+ i = wrap_i(i, v.size());
+ return v[(SizeType)i];
},
return_value_policy::reference_internal // ref + keepalive
);
void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl) {
using T = typename Vector::value_type;
using SizeType = typename Vector::size_type;
+ using DiffType = typename Vector::difference_type;
using ItType = typename Vector::iterator;
cl.def("__getitem__",
- [](const Vector &v, SizeType i) -> T {
- if (i >= v.size())
+ [](const Vector &v, DiffType i) -> T {
+ if (i < 0 && (i += v.size()) < 0)
throw index_error();
- return v[i];
+ if ((SizeType)i >= v.size())
+ throw index_error();
+ return v[(SizeType)i];
}
);
return_value_policy::reference_internal // ref + keepalive
);
+ cl.def("__contains__",
+ [](Map &m, const KeyType &k) -> bool {
+ auto it = m.find(k);
+ if (it == m.end())
+ return false;
+ return true;
+ }
+ );
+
// Assignment provided only if the type is copyable
detail::map_assignment<Map, Class_>(cl);
if(NOT PYTHONINTERP_FOUND)
set(PYTHONLIBS_FOUND FALSE)
+ set(PythonLibsNew_FOUND FALSE)
return()
endif()
"Python config failure:\n${_PYTHON_ERROR_VALUE}")
endif()
set(PYTHONLIBS_FOUND FALSE)
+ set(PythonLibsNew_FOUND FALSE)
return()
endif()
# Convert the process output into a list
+if(WIN32)
+ string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES})
+endif()
string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES})
string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES})
list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST)
"chosen compiler is ${_CMAKE_BITS}-bit")
endif()
set(PYTHONLIBS_FOUND FALSE)
+ set(PythonLibsNew_FOUND FALSE)
return()
endif()
string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR ${PYTHON_INCLUDE_DIR})
string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES})
-if(CMAKE_HOST_WIN32)
+if(CMAKE_HOST_WIN32 AND NOT (MSYS OR MINGW))
set(PYTHON_LIBRARY
"${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib")
"${PYTHON_EXECUTABLE}${PYTHON_VERSION}")
set(PYTHONLIBS_FOUND TRUE)
+set(PythonLibsNew_FOUND TRUE)
from clang import cindex
from clang.cindex import CursorKind
from collections import OrderedDict
+from glob import glob
from threading import Thread, Semaphore
from multiprocessing import cpu_count
CursorKind.FIELD_DECL
]
+PREFIX_BLACKLIST = [
+ CursorKind.TRANSLATION_UNIT
+]
+
CPP_OPERATORS = {
'<=': 'le', '>=': 'ge', '==': 'eq', '!=': 'ne', '[]': 'array',
'+=': 'iadd', '-=': 'isub', '*=': 'imul', '/=': 'idiv', '%=':
job_count = cpu_count()
job_semaphore = Semaphore(job_count)
-output = []
+
+class NoFilenamesError(ValueError):
+ pass
+
def d(s):
- return s.decode('utf8')
+ return s if isinstance(s, str) else s.decode('utf8')
def sanitize_name(name):
return result.rstrip().lstrip('\n')
-def extract(filename, node, prefix):
+def extract(filename, node, prefix, output):
if not (node.location.file is None or
os.path.samefile(d(node.location.file.name), filename)):
return 0
if node.kind in RECURSE_LIST:
sub_prefix = prefix
- if node.kind != CursorKind.TRANSLATION_UNIT:
+ if node.kind not in PREFIX_BLACKLIST:
if len(sub_prefix) > 0:
sub_prefix += '_'
sub_prefix += d(node.spelling)
for i in node.get_children():
- extract(filename, i, sub_prefix)
+ extract(filename, i, sub_prefix, output)
if node.kind in PRINT_LIST:
comment = d(node.raw_comment) if node.raw_comment is not None else ''
comment = process_comment(comment)
sub_prefix += '_'
if len(node.spelling) > 0:
name = sanitize_name(sub_prefix + d(node.spelling))
- global output
output.append((name, filename, comment))
class ExtractionThread(Thread):
- def __init__(self, filename, parameters):
+ def __init__(self, filename, parameters, output):
Thread.__init__(self)
self.filename = filename
self.parameters = parameters
+ self.output = output
job_semaphore.acquire()
def run(self):
index = cindex.Index(
cindex.conf.lib.clang_createIndex(False, True))
tu = index.parse(self.filename, self.parameters)
- extract(self.filename, tu.cursor, '')
+ extract(self.filename, tu.cursor, '', self.output)
finally:
job_semaphore.release()
-if __name__ == '__main__':
- parameters = ['-x', 'c++', '-std=c++11']
+
+def read_args(args):
+ parameters = []
filenames = []
+ if "-x" not in args:
+ parameters.extend(['-x', 'c++'])
+ if not any(it.startswith("-std=") for it in args):
+ parameters.append('-std=c++11')
if platform.system() == 'Darwin':
dev_path = '/Applications/Xcode.app/Contents/Developer/'
sysroot_dir = os.path.join(sdk_dir, next(os.walk(sdk_dir))[1][0])
parameters.append('-isysroot')
parameters.append(sysroot_dir)
-
- for item in sys.argv[1:]:
+ elif platform.system() == 'Linux':
+ # clang doesn't find its own base includes by default on Linux,
+ # but different distros install them in different paths.
+ # Try to autodetect, preferring the highest numbered version.
+ def clang_folder_version(d):
+ return [int(ver) for ver in re.findall(r'(?<!lib)(?<!\d)\d+', d)]
+ clang_include_dir = max((
+ path
+ for libdir in ['lib64', 'lib', 'lib32']
+ for path in glob('/usr/%s/clang/*/include' % libdir)
+ if os.path.isdir(path)
+ ), default=None, key=clang_folder_version)
+ if clang_include_dir:
+ parameters.extend(['-isystem', clang_include_dir])
+
+ for item in args:
if item.startswith('-'):
parameters.append(item)
else:
filenames.append(item)
if len(filenames) == 0:
- print('Syntax: %s [.. a list of header files ..]' % sys.argv[0])
- exit(-1)
+ raise NoFilenamesError("args parameter did not contain any filenames")
+
+ return parameters, filenames
+
+
+def extract_all(args):
+ parameters, filenames = read_args(args)
+ output = []
+ for filename in filenames:
+ thr = ExtractionThread(filename, parameters, output)
+ thr.start()
+
+ print('Waiting for jobs to finish ..', file=sys.stderr)
+ for i in range(job_count):
+ job_semaphore.acquire()
+ return output
+
+
+def write_header(comments, out_file=sys.stdout):
print('''/*
This file contains docstrings for the Python bindings.
Do not edit! These were automatically extracted by mkdoc.py
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
-''')
+''', file=out_file)
- output.clear()
- for filename in filenames:
- thr = ExtractionThread(filename, parameters)
- thr.start()
-
- print('Waiting for jobs to finish ..', file=sys.stderr)
- for i in range(job_count):
- job_semaphore.acquire()
name_ctr = 1
name_prev = None
- for name, _, comment in list(sorted(output, key=lambda x: (x[0], x[1]))):
+ for name, _, comment in list(sorted(comments, key=lambda x: (x[0], x[1]))):
if name == name_prev:
name_ctr += 1
name = name + "_%i" % name_ctr
name_prev = name
name_ctr = 1
print('\nstatic const char *%s =%sR"doc(%s)doc";' %
- (name, '\n' if '\n' in comment else ' ', comment))
+ (name, '\n' if '\n' in comment else ' ', comment), file=out_file)
print('''
#if defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
-''')
+''', file=out_file)
+
+
+def mkdoc(args):
+ args = list(args)
+ out_path = None
+ for idx, arg in enumerate(args):
+ if arg.startswith("-o"):
+ args.remove(arg)
+ try:
+ out_path = arg[2:] or args.pop(idx)
+ except IndexError:
+ print("-o flag requires an argument")
+ exit(-1)
+ break
+
+ comments = extract_all(args)
+
+ if out_path:
+ try:
+ with open(out_path, 'w') as out_file:
+ write_header(comments, out_file)
+ except:
+ # In the event of an error, don't leave a partially-written
+ # output file.
+ try:
+ os.unlink(out_path)
+ except:
+ pass
+ raise
+ else:
+ write_header(comments)
+
+
+if __name__ == '__main__':
+ try:
+ mkdoc(sys.argv[1:])
+ except NoFilenamesError:
+ print('Syntax: %s [.. a list of header files ..]' % sys.argv[0])
+ exit(-1)
set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES})
endif()
- set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}")
+ if(CMAKE_VERSION VERSION_LESS 3.3)
+ set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}")
+ else()
+ set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS $<$<COMPILE_LANGUAGE:CXX>:${PYBIND11_CPP_STANDARD}>)
+ endif()
get_property(_iid TARGET ${PN}::pybind11 PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
get_property(_ill TARGET ${PN}::module PROPERTY INTERFACE_LINK_LIBRARIES)
# Build a Python extension module:
# pybind11_add_module(<name> [MODULE | SHARED] [EXCLUDE_FROM_ALL]
-# [NO_EXTRAS] [THIN_LTO] source1 [source2 ...])
+# [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...])
#
function(pybind11_add_module target_name)
- set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS THIN_LTO)
+ set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO)
cmake_parse_arguments(ARG "${options}" "" "" ${ARGN})
if(ARG_MODULE AND ARG_SHARED)
add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS})
- target_include_directories(${target_name}
+ if(ARG_SYSTEM)
+ set(inc_isystem SYSTEM)
+ endif()
+
+ target_include_directories(${target_name} ${inc_isystem}
PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt
PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config
PRIVATE ${PYTHON_INCLUDE_DIRS})
# namespace; also turning it on for a pybind module compilation here avoids
# potential warnings or issues from having mixed hidden/non-hidden types.
set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden")
+ set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden")
if(WIN32 OR CYGWIN)
# Link against the Python shared library on Windows
endif()
# Make sure C++11/14 are enabled
- target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD})
+ if(CMAKE_VERSION VERSION_LESS 3.3)
+ target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD})
+ else()
+ target_compile_options(${target_name} PUBLIC $<$<COMPILE_LANGUAGE:CXX>:${PYBIND11_CPP_STANDARD}>)
+ endif()
if(ARG_NO_EXTRAS)
return()
_pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO})
- if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug)
+ if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo)
# Strip unnecessary sections of the binary on Linux/Mac OS
if(CMAKE_STRIP)
if(APPLE)
if(MSVC)
# /MP enables multithreaded builds (relevant when there are many files), /bigobj is
# needed for bigger binding projects due to the limit to 64k addressable sections
- target_compile_options(${target_name} PRIVATE /MP /bigobj)
+ target_compile_options(${target_name} PRIVATE /bigobj)
+ if(CMAKE_VERSION VERSION_LESS 3.11)
+ target_compile_options(${target_name} PRIVATE $<$<NOT:$<CONFIG:Debug>>:/MP>)
+ else()
+ # Only set these options for C++ files. This is important so that, for
+ # instance, projects that include other types of source files like CUDA
+ # .cu files don't get these options propagated to nvcc since that would
+ # cause the build to fail.
+ target_compile_options(${target_name} PRIVATE $<$<NOT:$<CONFIG:Debug>>:$<$<COMPILE_LANGUAGE:CXX>:/MP>>)
+ endif()
endif()
endfunction()