mirror of
https://github.com/RYDE-WORK/pybind11.git
synced 2026-01-19 13:13:19 +08:00
Fully-automatic clang-format with include reordering (#3713)
* chore: add clang-format
* Removing check-style (Classic check-style)
Ported from @henryiii's 53056b1b0e
* Automatic clang-format changes (NO manual changes).
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
This commit is contained in:
parent
e96221beff
commit
ec24786eab
@ -141,11 +141,7 @@ repos:
|
||||
entry: PyBind|Numpy|Cmake|CCache|PyTest
|
||||
exclude: .pre-commit-config.yaml
|
||||
|
||||
- repo: local
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: "v13.0.0"
|
||||
hooks:
|
||||
- id: check-style
|
||||
name: Classic check-style
|
||||
language: system
|
||||
types:
|
||||
- c++
|
||||
entry: ./tools/check-style.sh
|
||||
- id: clang-format
|
||||
|
||||
@ -20,54 +20,62 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
/// @{
|
||||
|
||||
/// Annotation for methods
|
||||
struct is_method { handle class_;
|
||||
struct is_method {
|
||||
handle class_;
|
||||
explicit is_method(const handle &c) : class_(c) {}
|
||||
};
|
||||
|
||||
/// Annotation for operators
|
||||
struct is_operator { };
|
||||
struct is_operator {};
|
||||
|
||||
/// Annotation for classes that cannot be subclassed
|
||||
struct is_final { };
|
||||
struct is_final {};
|
||||
|
||||
/// Annotation for parent scope
|
||||
struct scope { handle value;
|
||||
struct scope {
|
||||
handle value;
|
||||
explicit scope(const handle &s) : value(s) {}
|
||||
};
|
||||
|
||||
/// Annotation for documentation
|
||||
struct doc { const char *value;
|
||||
struct doc {
|
||||
const char *value;
|
||||
explicit doc(const char *value) : value(value) {}
|
||||
};
|
||||
|
||||
/// Annotation for function names
|
||||
struct name { const char *value;
|
||||
struct name {
|
||||
const char *value;
|
||||
explicit name(const char *value) : value(value) {}
|
||||
};
|
||||
|
||||
/// Annotation indicating that a function is an overload associated with a given "sibling"
|
||||
struct sibling { handle value;
|
||||
struct sibling {
|
||||
handle value;
|
||||
explicit sibling(const handle &value) : value(value.ptr()) {}
|
||||
};
|
||||
|
||||
/// Annotation indicating that a class derives from another given type
|
||||
template <typename T> struct base {
|
||||
template <typename T>
|
||||
struct base {
|
||||
|
||||
PYBIND11_DEPRECATED("base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
|
||||
base() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
|
||||
PYBIND11_DEPRECATED(
|
||||
"base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
|
||||
base() {} // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
|
||||
};
|
||||
|
||||
/// Keep patient alive while nurse lives
|
||||
template <size_t Nurse, size_t Patient> struct keep_alive { };
|
||||
template <size_t Nurse, size_t Patient>
|
||||
struct keep_alive {};
|
||||
|
||||
/// Annotation indicating that a class is involved in a multiple inheritance relationship
|
||||
struct multiple_inheritance { };
|
||||
struct multiple_inheritance {};
|
||||
|
||||
/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class
|
||||
struct dynamic_attr { };
|
||||
struct dynamic_attr {};
|
||||
|
||||
/// Annotation which enables the buffer protocol for a type
|
||||
struct buffer_protocol { };
|
||||
struct buffer_protocol {};
|
||||
|
||||
/// Annotation which requests that a special metaclass is created for a type
|
||||
struct metaclass {
|
||||
@ -78,7 +86,7 @@ struct metaclass {
|
||||
metaclass() {}
|
||||
|
||||
/// Override pybind11's default metaclass
|
||||
explicit metaclass(handle value) : value(value) { }
|
||||
explicit metaclass(handle value) : value(value) {}
|
||||
};
|
||||
|
||||
/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that
|
||||
@ -99,15 +107,16 @@ struct custom_type_setup {
|
||||
};
|
||||
|
||||
/// Annotation that marks a class as local to the module:
|
||||
struct module_local { const bool value;
|
||||
struct module_local {
|
||||
const bool value;
|
||||
constexpr explicit module_local(bool v = true) : value(v) {}
|
||||
};
|
||||
|
||||
/// Annotation to mark enums as an arithmetic type
|
||||
struct arithmetic { };
|
||||
struct arithmetic {};
|
||||
|
||||
/// Mark a function for addition at the beginning of the existing overload chain instead of the end
|
||||
struct prepend { };
|
||||
struct prepend {};
|
||||
|
||||
/** \rst
|
||||
A call policy which places one or more guard variables (``Ts...``) around the function call.
|
||||
@ -127,9 +136,13 @@ struct prepend { };
|
||||
return foo(args...); // forwarded arguments
|
||||
});
|
||||
\endrst */
|
||||
template <typename... Ts> struct call_guard;
|
||||
template <typename... Ts>
|
||||
struct call_guard;
|
||||
|
||||
template <> struct call_guard<> { using type = detail::void_type; };
|
||||
template <>
|
||||
struct call_guard<> {
|
||||
using type = detail::void_type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct call_guard<T> {
|
||||
@ -154,7 +167,8 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
enum op_id : int;
|
||||
enum op_type : int;
|
||||
struct undefined_t;
|
||||
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
|
||||
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t>
|
||||
struct op_;
|
||||
void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
|
||||
|
||||
/// Internal data structure which holds metadata about a keyword argument
|
||||
@ -166,15 +180,16 @@ struct argument_record {
|
||||
bool none : 1; ///< True if None is allowed when loading
|
||||
|
||||
argument_record(const char *name, const char *descr, handle value, bool convert, bool none)
|
||||
: name(name), descr(descr), value(value), convert(convert), none(none) { }
|
||||
: name(name), descr(descr), value(value), convert(convert), none(none) {}
|
||||
};
|
||||
|
||||
/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.)
|
||||
/// Internal data structure which holds metadata about a bound function (signature, overloads,
|
||||
/// etc.)
|
||||
struct function_record {
|
||||
function_record()
|
||||
: is_constructor(false), is_new_style_constructor(false), is_stateless(false),
|
||||
is_operator(false), is_method(false), has_args(false),
|
||||
has_kwargs(false), prepend(false) { }
|
||||
is_operator(false), is_method(false), has_args(false), has_kwargs(false),
|
||||
prepend(false) {}
|
||||
|
||||
/// Function name
|
||||
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
|
||||
@ -189,13 +204,13 @@ struct function_record {
|
||||
std::vector<argument_record> args;
|
||||
|
||||
/// Pointer to lambda function which converts arguments and performs the actual call
|
||||
handle (*impl) (function_call &) = nullptr;
|
||||
handle (*impl)(function_call &) = nullptr;
|
||||
|
||||
/// Storage for the wrapped function pointer and captured data, if any
|
||||
void *data[3] = { };
|
||||
void *data[3] = {};
|
||||
|
||||
/// Pointer to custom destructor for 'data' (if needed)
|
||||
void (*free_data) (function_record *ptr) = nullptr;
|
||||
void (*free_data)(function_record *ptr) = nullptr;
|
||||
|
||||
/// Return value policy associated with this function
|
||||
return_value_policy policy = return_value_policy::automatic;
|
||||
@ -251,7 +266,7 @@ struct function_record {
|
||||
struct type_record {
|
||||
PYBIND11_NOINLINE type_record()
|
||||
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false),
|
||||
default_holder(true), module_local(false), is_final(false) { }
|
||||
default_holder(true), module_local(false), is_final(false) {}
|
||||
|
||||
/// Handle to the parent scope
|
||||
handle scope;
|
||||
@ -310,22 +325,22 @@ struct type_record {
|
||||
/// Is the class inheritable from python classes?
|
||||
bool is_final : 1;
|
||||
|
||||
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) {
|
||||
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) {
|
||||
auto *base_info = detail::get_type_info(base, false);
|
||||
if (!base_info) {
|
||||
std::string tname(base.name());
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("generic_type: type \"" + std::string(name) +
|
||||
"\" referenced unknown base type \"" + tname + "\"");
|
||||
pybind11_fail("generic_type: type \"" + std::string(name)
|
||||
+ "\" referenced unknown base type \"" + tname + "\"");
|
||||
}
|
||||
|
||||
if (default_holder != base_info->default_holder) {
|
||||
std::string tname(base.name());
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("generic_type: type \"" + std::string(name) + "\" " +
|
||||
(default_holder ? "does not have" : "has") +
|
||||
" a non-default holder type while its base \"" + tname + "\" " +
|
||||
(base_info->default_holder ? "does not" : "does"));
|
||||
pybind11_fail("generic_type: type \"" + std::string(name) + "\" "
|
||||
+ (default_holder ? "does not have" : "has")
|
||||
+ " a non-default holder type while its base \"" + tname + "\" "
|
||||
+ (base_info->default_holder ? "does not" : "does"));
|
||||
}
|
||||
|
||||
bases.append((PyObject *) base_info->type);
|
||||
@ -340,14 +355,13 @@ struct type_record {
|
||||
}
|
||||
};
|
||||
|
||||
inline function_call::function_call(const function_record &f, handle p) :
|
||||
func(f), parent(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);
|
||||
}
|
||||
|
||||
/// Tag for a new-style `__init__` defined in `detail/init.h`
|
||||
struct is_new_style_constructor { };
|
||||
struct is_new_style_constructor {};
|
||||
|
||||
/**
|
||||
* Partial template specializations to process custom attributes provided to
|
||||
@ -355,60 +369,79 @@ struct is_new_style_constructor { };
|
||||
* fields in the type_record and function_record data structures or executed at
|
||||
* runtime to deal with custom call policies (e.g. keep_alive).
|
||||
*/
|
||||
template <typename T, typename SFINAE = void> struct process_attribute;
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct process_attribute;
|
||||
|
||||
template <typename T> struct process_attribute_default {
|
||||
template <typename T>
|
||||
struct process_attribute_default {
|
||||
/// Default implementation: do nothing
|
||||
static void init(const T &, function_record *) { }
|
||||
static void init(const T &, type_record *) { }
|
||||
static void precall(function_call &) { }
|
||||
static void postcall(function_call &, handle) { }
|
||||
static void init(const T &, function_record *) {}
|
||||
static void init(const T &, type_record *) {}
|
||||
static void precall(function_call &) {}
|
||||
static void postcall(function_call &, handle) {}
|
||||
};
|
||||
|
||||
/// Process an attribute specifying the function's name
|
||||
template <> struct process_attribute<name> : process_attribute_default<name> {
|
||||
template <>
|
||||
struct process_attribute<name> : process_attribute_default<name> {
|
||||
static void init(const name &n, function_record *r) { r->name = const_cast<char *>(n.value); }
|
||||
};
|
||||
|
||||
/// Process an attribute specifying the function's docstring
|
||||
template <> struct process_attribute<doc> : process_attribute_default<doc> {
|
||||
template <>
|
||||
struct process_attribute<doc> : process_attribute_default<doc> {
|
||||
static void init(const doc &n, function_record *r) { r->doc = const_cast<char *>(n.value); }
|
||||
};
|
||||
|
||||
/// Process an attribute specifying the function's docstring (provided as a C-style string)
|
||||
template <> struct process_attribute<const char *> : process_attribute_default<const char *> {
|
||||
template <>
|
||||
struct process_attribute<const char *> : process_attribute_default<const char *> {
|
||||
static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); }
|
||||
static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); }
|
||||
};
|
||||
template <> struct process_attribute<char *> : process_attribute<const char *> { };
|
||||
template <>
|
||||
struct process_attribute<char *> : process_attribute<const char *> {};
|
||||
|
||||
/// Process an attribute indicating the function's return value policy
|
||||
template <> struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> {
|
||||
template <>
|
||||
struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> {
|
||||
static void init(const return_value_policy &p, function_record *r) { r->policy = p; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates that this is an overloaded function associated with a given sibling
|
||||
template <> struct process_attribute<sibling> : process_attribute_default<sibling> {
|
||||
/// Process an attribute which indicates that this is an overloaded function associated with a
|
||||
/// given sibling
|
||||
template <>
|
||||
struct process_attribute<sibling> : process_attribute_default<sibling> {
|
||||
static void init(const sibling &s, function_record *r) { r->sibling = s.value; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates that this function is a method
|
||||
template <> struct process_attribute<is_method> : process_attribute_default<is_method> {
|
||||
static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; }
|
||||
template <>
|
||||
struct process_attribute<is_method> : process_attribute_default<is_method> {
|
||||
static void init(const is_method &s, function_record *r) {
|
||||
r->is_method = true;
|
||||
r->scope = s.class_;
|
||||
}
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates the parent scope of a method
|
||||
template <> struct process_attribute<scope> : process_attribute_default<scope> {
|
||||
template <>
|
||||
struct process_attribute<scope> : process_attribute_default<scope> {
|
||||
static void init(const scope &s, function_record *r) { r->scope = s.value; }
|
||||
};
|
||||
|
||||
/// Process an attribute which indicates that this function is an operator
|
||||
template <> struct process_attribute<is_operator> : process_attribute_default<is_operator> {
|
||||
template <>
|
||||
struct process_attribute<is_operator> : process_attribute_default<is_operator> {
|
||||
static void init(const is_operator &, function_record *r) { r->is_operator = true; }
|
||||
};
|
||||
|
||||
template <> struct process_attribute<is_new_style_constructor> : process_attribute_default<is_new_style_constructor> {
|
||||
static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; }
|
||||
template <>
|
||||
struct process_attribute<is_new_style_constructor>
|
||||
: process_attribute_default<is_new_style_constructor> {
|
||||
static void init(const is_new_style_constructor &, function_record *r) {
|
||||
r->is_new_style_constructor = true;
|
||||
}
|
||||
};
|
||||
|
||||
inline void check_kw_only_arg(const arg &a, function_record *r) {
|
||||
@ -425,7 +458,8 @@ inline void append_self_arg_if_needed(function_record *r) {
|
||||
}
|
||||
|
||||
/// Process a keyword argument attribute (*without* a default value)
|
||||
template <> struct process_attribute<arg> : process_attribute_default<arg> {
|
||||
template <>
|
||||
struct process_attribute<arg> : process_attribute_default<arg> {
|
||||
static void init(const arg &a, function_record *r) {
|
||||
append_self_arg_if_needed(r);
|
||||
r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none);
|
||||
@ -435,7 +469,8 @@ template <> struct process_attribute<arg> : process_attribute_default<arg> {
|
||||
};
|
||||
|
||||
/// Process a keyword argument attribute (*with* a default value)
|
||||
template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
||||
template <>
|
||||
struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
||||
static void init(const arg_v &a, function_record *r) {
|
||||
if (r->is_method && r->args.empty()) {
|
||||
r->args.emplace_back(
|
||||
@ -451,15 +486,16 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
||||
descr += a.type + "'";
|
||||
if (r->is_method) {
|
||||
if (r->name) {
|
||||
descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'";
|
||||
descr += " in method '" + (std::string) str(r->scope) + "."
|
||||
+ (std::string) r->name + "'";
|
||||
} else {
|
||||
descr += " in method of '" + (std::string) str(r->scope) + "'";
|
||||
}
|
||||
} else if (r->name) {
|
||||
descr += " in function '" + (std::string) r->name + "'";
|
||||
}
|
||||
pybind11_fail("arg(): could not convert default argument "
|
||||
+ descr + " into a Python object (type not registered yet?)");
|
||||
pybind11_fail("arg(): could not convert default argument " + descr
|
||||
+ " into a Python object (type not registered yet?)");
|
||||
#else
|
||||
pybind11_fail("arg(): could not convert default argument "
|
||||
"into a Python object (type not registered yet?). "
|
||||
@ -473,7 +509,8 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
||||
};
|
||||
|
||||
/// Process a keyword-only-arguments-follow pseudo argument
|
||||
template <> struct process_attribute<kw_only> : process_attribute_default<kw_only> {
|
||||
template <>
|
||||
struct process_attribute<kw_only> : process_attribute_default<kw_only> {
|
||||
static void init(const kw_only &, function_record *r) {
|
||||
append_self_arg_if_needed(r);
|
||||
if (r->has_args && r->nargs_pos != static_cast<std::uint16_t>(r->args.size())) {
|
||||
@ -485,20 +522,23 @@ template <> struct process_attribute<kw_only> : process_attribute_default<kw_onl
|
||||
};
|
||||
|
||||
/// Process a positional-only-argument maker
|
||||
template <> struct process_attribute<pos_only> : process_attribute_default<pos_only> {
|
||||
template <>
|
||||
struct process_attribute<pos_only> : process_attribute_default<pos_only> {
|
||||
static void init(const pos_only &, function_record *r) {
|
||||
append_self_arg_if_needed(r);
|
||||
r->nargs_pos_only = static_cast<std::uint16_t>(r->args.size());
|
||||
if (r->nargs_pos_only > r->nargs_pos) {
|
||||
pybind11_fail("pos_only(): cannot follow a py::args() argument");
|
||||
}
|
||||
// It also can't follow a kw_only, but a static_assert in pybind11.h checks that
|
||||
// It also can't follow a kw_only, but a static_assert in pybind11.h checks that
|
||||
}
|
||||
};
|
||||
|
||||
/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that)
|
||||
/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees
|
||||
/// that)
|
||||
template <typename T>
|
||||
struct process_attribute<T, enable_if_t<is_pyobject<T>::value>> : process_attribute_default<handle> {
|
||||
struct process_attribute<T, enable_if_t<is_pyobject<T>::value>>
|
||||
: process_attribute_default<handle> {
|
||||
static void init(const handle &h, type_record *r) { r->bases.append(h); }
|
||||
};
|
||||
|
||||
@ -511,7 +551,9 @@ struct process_attribute<base<T>> : process_attribute_default<base<T>> {
|
||||
/// Process a multiple inheritance attribute
|
||||
template <>
|
||||
struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> {
|
||||
static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; }
|
||||
static void init(const multiple_inheritance &, type_record *r) {
|
||||
r->multiple_inheritance = true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
@ -557,34 +599,41 @@ template <>
|
||||
struct process_attribute<arithmetic> : process_attribute_default<arithmetic> {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_guard<Ts...>> { };
|
||||
struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_guard<Ts...>> {};
|
||||
|
||||
/**
|
||||
* Process a keep_alive call policy -- invokes keep_alive_impl during the
|
||||
* pre-call handler if both Nurse, Patient != 0 and use the post-call handler
|
||||
* otherwise
|
||||
*/
|
||||
template <size_t Nurse, size_t Patient> struct process_attribute<keep_alive<Nurse, Patient>> : public process_attribute_default<keep_alive<Nurse, Patient>> {
|
||||
template <size_t Nurse, size_t Patient>
|
||||
struct process_attribute<keep_alive<Nurse, Patient>>
|
||||
: public process_attribute_default<keep_alive<Nurse, Patient>> {
|
||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
|
||||
static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); }
|
||||
static void precall(function_call &call) {
|
||||
keep_alive_impl(Nurse, Patient, call, handle());
|
||||
}
|
||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
|
||||
static void postcall(function_call &, handle) { }
|
||||
static void postcall(function_call &, handle) {}
|
||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
|
||||
static void precall(function_call &) { }
|
||||
static void precall(function_call &) {}
|
||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
|
||||
static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); }
|
||||
static void postcall(function_call &call, handle ret) {
|
||||
keep_alive_impl(Nurse, Patient, call, ret);
|
||||
}
|
||||
};
|
||||
|
||||
/// Recursively iterate over variadic template arguments
|
||||
template <typename... Args> struct process_attributes {
|
||||
static void init(const Args&... args, function_record *r) {
|
||||
template <typename... Args>
|
||||
struct process_attributes {
|
||||
static void init(const Args &...args, function_record *r) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
||||
using expander = int[];
|
||||
(void) expander{
|
||||
0, ((void) process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
||||
}
|
||||
static void init(const Args&... args, type_record *r) {
|
||||
static void init(const Args &...args, type_record *r) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
||||
using expander = int[];
|
||||
@ -616,7 +665,7 @@ using extract_guard_t = typename exactly_one_t<is_call_guard, call_guard<>, Extr
|
||||
/// Check the number of named arguments at compile time
|
||||
template <typename... Extra,
|
||||
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
|
||||
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
|
||||
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
|
||||
constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs);
|
||||
return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs;
|
||||
|
||||
@ -44,8 +44,8 @@ struct buffer_info {
|
||||
void *ptr = nullptr; // Pointer to the underlying storage
|
||||
ssize_t itemsize = 0; // Size of individual items in bytes
|
||||
ssize_t size = 0; // Total number of entries
|
||||
std::string format; // For homogeneous buffers, this should be set to
|
||||
// format_descriptor<T>::format()
|
||||
std::string format; // For homogeneous buffers, this should be set to
|
||||
// format_descriptor<T>::format()
|
||||
ssize_t ndim = 0; // Number of dimensions
|
||||
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
|
||||
std::vector<ssize_t> strides; // Number of bytes between adjacent entries
|
||||
@ -54,10 +54,15 @@ struct buffer_info {
|
||||
|
||||
buffer_info() = default;
|
||||
|
||||
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
|
||||
detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
|
||||
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
|
||||
shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
|
||||
buffer_info(void *ptr,
|
||||
ssize_t itemsize,
|
||||
const std::string &format,
|
||||
ssize_t ndim,
|
||||
detail::any_container<ssize_t> shape_in,
|
||||
detail::any_container<ssize_t> strides_in,
|
||||
bool readonly = false)
|
||||
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
|
||||
shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
|
||||
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) {
|
||||
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
|
||||
}
|
||||
@ -67,29 +72,48 @@ struct buffer_info {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
|
||||
: buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { }
|
||||
buffer_info(T *ptr,
|
||||
detail::any_container<ssize_t> shape_in,
|
||||
detail::any_container<ssize_t> strides_in,
|
||||
bool readonly = false)
|
||||
: buffer_info(private_ctr_tag(),
|
||||
ptr,
|
||||
sizeof(T),
|
||||
format_descriptor<T>::format(),
|
||||
static_cast<ssize_t>(shape_in->size()),
|
||||
std::move(shape_in),
|
||||
std::move(strides_in),
|
||||
readonly) {}
|
||||
|
||||
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false)
|
||||
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { }
|
||||
buffer_info(void *ptr,
|
||||
ssize_t itemsize,
|
||||
const std::string &format,
|
||||
ssize_t size,
|
||||
bool readonly = false)
|
||||
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {}
|
||||
|
||||
template <typename T>
|
||||
buffer_info(T *ptr, ssize_t size, bool readonly=false)
|
||||
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) { }
|
||||
buffer_info(T *ptr, ssize_t size, bool readonly = false)
|
||||
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) {}
|
||||
|
||||
template <typename T>
|
||||
buffer_info(const T *ptr, ssize_t size, bool readonly=true)
|
||||
: buffer_info(const_cast<T*>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) { }
|
||||
buffer_info(const T *ptr, ssize_t size, bool readonly = true)
|
||||
: buffer_info(
|
||||
const_cast<T *>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) {}
|
||||
|
||||
explicit buffer_info(Py_buffer *view, bool ownview = true)
|
||||
: buffer_info(view->buf, view->itemsize, view->format, view->ndim,
|
||||
: buffer_info(
|
||||
view->buf,
|
||||
view->itemsize,
|
||||
view->format,
|
||||
view->ndim,
|
||||
{view->shape, view->shape + view->ndim},
|
||||
/* Though buffer::request() requests PyBUF_STRIDES, ctypes objects
|
||||
* ignore this flag and return a view with NULL strides.
|
||||
* When strides are NULL, build them manually. */
|
||||
view->strides
|
||||
? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
|
||||
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
|
||||
? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
|
||||
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
|
||||
(view->readonly != 0)) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
this->m_view = view;
|
||||
@ -98,7 +122,7 @@ struct buffer_info {
|
||||
}
|
||||
|
||||
buffer_info(const buffer_info &) = delete;
|
||||
buffer_info& operator=(const buffer_info &) = delete;
|
||||
buffer_info &operator=(const buffer_info &) = delete;
|
||||
|
||||
buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }
|
||||
|
||||
@ -117,17 +141,28 @@ struct buffer_info {
|
||||
}
|
||||
|
||||
~buffer_info() {
|
||||
if (m_view && ownview) { PyBuffer_Release(m_view); delete m_view; }
|
||||
if (m_view && ownview) {
|
||||
PyBuffer_Release(m_view);
|
||||
delete m_view;
|
||||
}
|
||||
}
|
||||
|
||||
Py_buffer *view() const { return m_view; }
|
||||
Py_buffer *&view() { return m_view; }
|
||||
private:
|
||||
struct private_ctr_tag { };
|
||||
|
||||
buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
|
||||
detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in, bool readonly)
|
||||
: buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { }
|
||||
private:
|
||||
struct private_ctr_tag {};
|
||||
|
||||
buffer_info(private_ctr_tag,
|
||||
void *ptr,
|
||||
ssize_t itemsize,
|
||||
const std::string &format,
|
||||
ssize_t ndim,
|
||||
detail::any_container<ssize_t> &&shape_in,
|
||||
detail::any_container<ssize_t> &&strides_in,
|
||||
bool readonly)
|
||||
: buffer_info(
|
||||
ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {}
|
||||
|
||||
Py_buffer *m_view = nullptr;
|
||||
bool ownview = false;
|
||||
@ -135,17 +170,22 @@ private:
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T, typename SFINAE = void> struct compare_buffer_info {
|
||||
static bool compare(const buffer_info& b) {
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct compare_buffer_info {
|
||||
static bool compare(const buffer_info &b) {
|
||||
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
|
||||
static bool compare(const buffer_info& b) {
|
||||
return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor<T>::value ||
|
||||
((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned<T>::value ? "L" : "l")) ||
|
||||
((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned<T>::value ? "N" : "n")));
|
||||
template <typename T>
|
||||
struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
|
||||
static bool compare(const buffer_info &b) {
|
||||
return (size_t) b.itemsize == sizeof(T)
|
||||
&& (b.format == format_descriptor<T>::value
|
||||
|| ((sizeof(T) == sizeof(long))
|
||||
&& b.format == (std::is_unsigned<T>::value ? "L" : "l"))
|
||||
|| ((sizeof(T) == sizeof(size_t))
|
||||
&& b.format == (std::is_unsigned<T>::value ? "N" : "n")));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -15,25 +15,25 @@
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
|
||||
#include <datetime.h>
|
||||
#include <mutex>
|
||||
|
||||
// Backport the PyDateTime_DELTA functions from Python3.3 if required
|
||||
#ifndef PyDateTime_DELTA_GET_DAYS
|
||||
#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days)
|
||||
# define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta *) o)->days)
|
||||
#endif
|
||||
#ifndef PyDateTime_DELTA_GET_SECONDS
|
||||
#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds)
|
||||
# define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta *) o)->seconds)
|
||||
#endif
|
||||
#ifndef PyDateTime_DELTA_GET_MICROSECONDS
|
||||
#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds)
|
||||
# define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta *) o)->microseconds)
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename type> class duration_caster {
|
||||
template <typename type>
|
||||
class duration_caster {
|
||||
public:
|
||||
using rep = typename type::rep;
|
||||
using period = typename type::period;
|
||||
@ -45,7 +45,9 @@ public:
|
||||
using namespace std::chrono;
|
||||
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
if (!PyDateTimeAPI) {
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!src) {
|
||||
return false;
|
||||
@ -53,26 +55,30 @@ public:
|
||||
// If invoked with datetime.delta object
|
||||
if (PyDelta_Check(src.ptr())) {
|
||||
value = type(duration_cast<duration<rep, period>>(
|
||||
days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
|
||||
days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
|
||||
+ seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
|
||||
+ microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
|
||||
return true;
|
||||
}
|
||||
// If invoked with a float we assume it is seconds and convert
|
||||
if (PyFloat_Check(src.ptr())) {
|
||||
value = type(duration_cast<duration<rep, period>>(duration<double>(PyFloat_AsDouble(src.ptr()))));
|
||||
value = type(duration_cast<duration<rep, period>>(
|
||||
duration<double>(PyFloat_AsDouble(src.ptr()))));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is a duration just return it back
|
||||
static const std::chrono::duration<rep, period>& get_duration(const std::chrono::duration<rep, period> &src) {
|
||||
static const std::chrono::duration<rep, period> &
|
||||
get_duration(const std::chrono::duration<rep, period> &src) {
|
||||
return src;
|
||||
}
|
||||
|
||||
// If this is a time_point get the time_since_epoch
|
||||
template <typename Clock> static std::chrono::duration<rep, period> get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) {
|
||||
template <typename Clock>
|
||||
static std::chrono::duration<rep, period>
|
||||
get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) {
|
||||
return src.time_since_epoch();
|
||||
}
|
||||
|
||||
@ -84,9 +90,12 @@ public:
|
||||
auto d = get_duration(src);
|
||||
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
if (!PyDateTimeAPI) {
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
// Declare these special duration types so the conversions happen with the correct primitive types (int)
|
||||
// Declare these special duration types so the conversions happen with the correct
|
||||
// primitive types (int)
|
||||
using dd_t = duration<int, std::ratio<86400>>;
|
||||
using ss_t = duration<int, std::ratio<1>>;
|
||||
using us_t = duration<int, std::micro>;
|
||||
@ -118,14 +127,17 @@ inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
|
||||
}
|
||||
|
||||
// This is for casting times on the system clock into datetime.datetime instances
|
||||
template <typename Duration> class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
||||
template <typename Duration>
|
||||
class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
||||
public:
|
||||
using type = std::chrono::time_point<std::chrono::system_clock, Duration>;
|
||||
bool load(handle src, bool) {
|
||||
using namespace std::chrono;
|
||||
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
if (!PyDateTimeAPI) {
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
if (!src) {
|
||||
return false;
|
||||
@ -135,32 +147,32 @@ public:
|
||||
microseconds msecs;
|
||||
|
||||
if (PyDateTime_Check(src.ptr())) {
|
||||
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_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_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_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(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
|
||||
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_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);
|
||||
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_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()));
|
||||
msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -169,14 +181,18 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src,
|
||||
return_value_policy /* policy */,
|
||||
handle /* parent */) {
|
||||
using namespace std::chrono;
|
||||
|
||||
// Lazy initialise the PyDateTime import
|
||||
if (!PyDateTimeAPI) { PyDateTime_IMPORT; }
|
||||
if (!PyDateTimeAPI) {
|
||||
PyDateTime_IMPORT;
|
||||
}
|
||||
|
||||
// Get out microseconds, and make sure they are positive, to avoid bug in eastern hemisphere time zones
|
||||
// (cfr. https://github.com/pybind/pybind11/issues/2417)
|
||||
// Get out microseconds, and make sure they are positive, to avoid bug in eastern
|
||||
// hemisphere time zones (cfr. https://github.com/pybind/pybind11/issues/2417)
|
||||
using us_t = duration<int, std::micro>;
|
||||
auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1));
|
||||
if (us.count() < 0) {
|
||||
@ -184,9 +200,10 @@ public:
|
||||
}
|
||||
|
||||
// Subtract microseconds BEFORE `system_clock::to_time_t`, because:
|
||||
// > If std::time_t has lower precision, it is implementation-defined whether the value is rounded or truncated.
|
||||
// (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
|
||||
std::time_t tt = system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
|
||||
// > If std::time_t has lower precision, it is implementation-defined whether the value is
|
||||
// rounded or truncated. (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
|
||||
std::time_t tt
|
||||
= system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
|
||||
|
||||
std::tm localtime;
|
||||
std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
|
||||
@ -207,13 +224,13 @@ public:
|
||||
// Other clocks that are not the system clock are not measured as datetime.datetime objects
|
||||
// since they are not measured on calendar time. So instead we just make them timedeltas
|
||||
// Or if they have passed us a time as a float we convert that
|
||||
template <typename Clock, typename Duration> class type_caster<std::chrono::time_point<Clock, Duration>>
|
||||
: public duration_caster<std::chrono::time_point<Clock, Duration>> {
|
||||
};
|
||||
template <typename Clock, typename Duration>
|
||||
class type_caster<std::chrono::time_point<Clock, Duration>>
|
||||
: public duration_caster<std::chrono::time_point<Clock, Duration>> {};
|
||||
|
||||
template <typename Rep, typename Period> class type_caster<std::chrono::duration<Rep, Period>>
|
||||
: public duration_caster<std::chrono::duration<Rep, Period>> {
|
||||
};
|
||||
template <typename Rep, typename Period>
|
||||
class type_caster<std::chrono::duration<Rep, Period>>
|
||||
: public duration_caster<std::chrono::duration<Rep, Period>> {};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
@ -10,36 +10,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <complex>
|
||||
|
||||
/// glibc defines I as a macro which breaks things, e.g., boost template names
|
||||
#ifdef I
|
||||
# undef I
|
||||
# undef I
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
template <typename T> struct format_descriptor<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
template <typename T>
|
||||
struct format_descriptor<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
static constexpr const char c = format_descriptor<T>::c;
|
||||
static constexpr const char value[3] = { 'Z', c, '\0' };
|
||||
static constexpr const char value[3] = {'Z', c, '\0'};
|
||||
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];
|
||||
template <typename T>
|
||||
constexpr const char
|
||||
format_descriptor<std::complex<T>,
|
||||
detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
|
||||
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T> struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
template <typename T>
|
||||
struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
static constexpr bool value = true;
|
||||
static constexpr int index = is_fmt_numeric<T>::index + 3;
|
||||
};
|
||||
|
||||
template <typename T> class type_caster<std::complex<T>> {
|
||||
template <typename T>
|
||||
class type_caster<std::complex<T>> {
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!src) {
|
||||
@ -57,7 +63,8 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const std::complex<T> &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
static handle
|
||||
cast(const std::complex<T> &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
return PyComplex_FromDoubles((double) src.real(), (double) src.imag());
|
||||
}
|
||||
|
||||
|
||||
@ -16,12 +16,13 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION)
|
||||
# define PYBIND11_BUILTIN_QUALNAME
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
|
||||
# define PYBIND11_BUILTIN_QUALNAME
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
|
||||
#else
|
||||
// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type
|
||||
// signatures; in 3.3+ this macro expands to nothing:
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj)
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) \
|
||||
setattr((PyObject *) obj, "__qualname__", nameobj)
|
||||
#endif
|
||||
|
||||
inline std::string get_fully_qualified_tp_name(PyTypeObject *type) {
|
||||
@ -71,9 +72,9 @@ inline PyTypeObject *make_static_property_type() {
|
||||
}
|
||||
|
||||
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
# ifdef PYBIND11_BUILTIN_QUALNAME
|
||||
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||
#endif
|
||||
# endif
|
||||
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_name = name;
|
||||
@ -107,8 +108,10 @@ inline PyTypeObject *make_static_property_type() {
|
||||
def __set__(self, obj, value):
|
||||
cls = obj if isinstance(obj, type) else type(obj)
|
||||
property.__set__(self, cls, value)
|
||||
)", Py_file_input, d.ptr(), d.ptr()
|
||||
);
|
||||
)",
|
||||
Py_file_input,
|
||||
d.ptr(),
|
||||
d.ptr());
|
||||
if (result == nullptr)
|
||||
throw error_already_set();
|
||||
Py_DECREF(result);
|
||||
@ -121,7 +124,7 @@ inline PyTypeObject *make_static_property_type() {
|
||||
By default, Python replaces the `static_property` itself, but for wrapped C++ types
|
||||
we need to call `static_property.__set__()` in order to propagate the new value to
|
||||
the underlying C++ data structure. */
|
||||
extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) {
|
||||
extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyObject *value) {
|
||||
// Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
|
||||
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
|
||||
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
|
||||
@ -184,7 +187,8 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
|
||||
// Ensure that the base __init__ function(s) were called
|
||||
for (const auto &vh : values_and_holders(instance)) {
|
||||
if (!vh.holder_constructed()) {
|
||||
PyErr_Format(PyExc_TypeError, "%.200s.__init__() must be called when overriding __init__",
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s.__init__() must be called when overriding __init__",
|
||||
get_fully_qualified_tp_name(vh.type->type).c_str());
|
||||
Py_DECREF(self);
|
||||
return nullptr;
|
||||
@ -203,9 +207,8 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
||||
// 1) be found in internals.registered_types_py
|
||||
// 2) have exactly one associated `detail::type_info`
|
||||
auto found_type = internals.registered_types_py.find(type);
|
||||
if (found_type != internals.registered_types_py.end() &&
|
||||
found_type->second.size() == 1 &&
|
||||
found_type->second[0]->type == type) {
|
||||
if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1
|
||||
&& found_type->second[0]->type == type) {
|
||||
|
||||
auto *tinfo = found_type->second[0];
|
||||
auto tindex = std::type_index(*tinfo->cpptype);
|
||||
@ -220,7 +223,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
||||
|
||||
// Actually just `std::erase_if`, but that's only available in C++20
|
||||
auto &cache = internals.inactive_override_cache;
|
||||
for (auto it = cache.begin(), last = cache.end(); it != last; ) {
|
||||
for (auto it = cache.begin(), last = cache.end(); it != last;) {
|
||||
if (it->first == (PyObject *) tinfo->type) {
|
||||
it = cache.erase(it);
|
||||
} else {
|
||||
@ -237,7 +240,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
||||
/** This metaclass is assigned by default to all pybind11 types and is required in order
|
||||
for static properties to function correctly. Users may override this using `py::metaclass`.
|
||||
Return value: New reference. */
|
||||
inline PyTypeObject* make_default_metaclass() {
|
||||
inline PyTypeObject *make_default_metaclass() {
|
||||
constexpr auto *name = "pybind11_type";
|
||||
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
|
||||
|
||||
@ -281,9 +284,12 @@ inline PyTypeObject* make_default_metaclass() {
|
||||
|
||||
/// For multiple inheritance types we need to recursively register/deregister base pointers for any
|
||||
/// base classes with pointers that are difference from the instance value pointer so that we can
|
||||
/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs.
|
||||
inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self,
|
||||
bool (*f)(void * /*parentptr*/, instance * /*self*/)) {
|
||||
/// correctly recognize an offset base class pointer. This calls a function with any offset base
|
||||
/// ptrs.
|
||||
inline void traverse_offset_bases(void *valueptr,
|
||||
const detail::type_info *tinfo,
|
||||
instance *self,
|
||||
bool (*f)(void * /*parentptr*/, instance * /*self*/)) {
|
||||
for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) {
|
||||
if (auto *parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) {
|
||||
for (auto &c : parent_tinfo->implicit_casts) {
|
||||
@ -331,13 +337,13 @@ inline bool deregister_instance(instance *self, void *valptr, const type_info *t
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Instance creation function for all pybind11 types. It allocates the internal instance layout for
|
||||
/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast
|
||||
/// to a reference or pointer), and initialization is done by an `__init__` function.
|
||||
/// Instance creation function for all pybind11 types. It allocates the internal instance layout
|
||||
/// for holding C++ objects and holders. Allocation is done lazily (the first time the instance is
|
||||
/// cast to a reference or pointer), and initialization is done by an `__init__` function.
|
||||
inline PyObject *make_new_instance(PyTypeObject *type) {
|
||||
#if defined(PYPY_VERSION)
|
||||
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
|
||||
// object is a plain Python type (i.e. not derived from an extension type). Fix it.
|
||||
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first
|
||||
// inherited object is a plain Python type (i.e. not derived from an extension type). Fix it.
|
||||
ssize_t instance_size = static_cast<ssize_t>(sizeof(instance));
|
||||
if (type->tp_basicsize < instance_size) {
|
||||
type->tp_basicsize = instance_size;
|
||||
@ -511,7 +517,8 @@ extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
|
||||
/// dynamic_attr: Support for `instance.__dict__ = dict()`.
|
||||
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
|
||||
if (!PyDict_Check(new_dict)) {
|
||||
PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'",
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"__dict__ must be set to a dictionary, not a '%.200s'",
|
||||
get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str());
|
||||
return -1;
|
||||
}
|
||||
@ -540,15 +547,14 @@ extern "C" inline int pybind11_clear(PyObject *self) {
|
||||
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
|
||||
type->tp_dictoffset = type->tp_basicsize; // place dict at the end
|
||||
type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it
|
||||
type->tp_dictoffset = type->tp_basicsize; // place dict at the end
|
||||
type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
|
||||
type->tp_traverse = pybind11_traverse;
|
||||
type->tp_clear = pybind11_clear;
|
||||
|
||||
static PyGetSetDef getset[] = {
|
||||
{const_cast<char*>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr}
|
||||
};
|
||||
{const_cast<char *>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr}};
|
||||
type->tp_getset = getset;
|
||||
}
|
||||
|
||||
@ -617,7 +623,7 @@ inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
|
||||
|
||||
/** Create a brand new Python type according to the `type_record` specification.
|
||||
Return value: New reference. */
|
||||
inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
inline PyObject *make_new_python_type(const type_record &rec) {
|
||||
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name));
|
||||
|
||||
auto qualname = name;
|
||||
@ -678,7 +684,7 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_name = full_name;
|
||||
type->tp_doc = tp_doc;
|
||||
type->tp_base = type_incref((PyTypeObject *)base);
|
||||
type->tp_base = type_incref((PyTypeObject *) base);
|
||||
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
|
||||
if (!bases.empty()) {
|
||||
type->tp_bases = bases.release().ptr();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -15,9 +15,9 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
# define PYBIND11_DESCR_CONSTEXPR static constexpr
|
||||
# define PYBIND11_DESCR_CONSTEXPR static constexpr
|
||||
#else
|
||||
# define PYBIND11_DESCR_CONSTEXPR const
|
||||
# define PYBIND11_DESCR_CONSTEXPR const
|
||||
#endif
|
||||
|
||||
/* Concatenate type signatures at compile time */
|
||||
@ -27,14 +27,14 @@ struct descr {
|
||||
|
||||
constexpr descr() = default;
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence<N>()) { }
|
||||
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'} { }
|
||||
constexpr descr(char const (&s)[N + 1], index_sequence<Is...>) : text{s[Is]..., '\0'} {}
|
||||
|
||||
template <typename... Chars>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} { }
|
||||
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}};
|
||||
@ -42,81 +42,106 @@ struct descr {
|
||||
};
|
||||
|
||||
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2>
|
||||
constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a, const descr<N2, Ts2...> &b,
|
||||
index_sequence<Is1...>, index_sequence<Is2...>) {
|
||||
constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a,
|
||||
const descr<N2, Ts2...> &b,
|
||||
index_sequence<Is1...>,
|
||||
index_sequence<Is2...>) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b);
|
||||
return {a.text[Is1]..., b.text[Is2]...};
|
||||
}
|
||||
|
||||
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) {
|
||||
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> const_name(char const(&text)[N]) { return descr<N - 1>(text); }
|
||||
constexpr descr<0> const_name(char const(&)[1]) { return {}; }
|
||||
constexpr descr<N - 1> const_name(char const (&text)[N]) {
|
||||
return descr<N - 1>(text);
|
||||
}
|
||||
constexpr descr<0> const_name(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...> {
|
||||
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...> {
|
||||
// WARNING: This only works with C++17 or higher.
|
||||
static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...);
|
||||
};
|
||||
|
||||
// Ternary description (like std::conditional)
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<B, descr<N1 - 1>> const_name(char const(&text1)[N1], char const(&)[N2]) {
|
||||
constexpr enable_if_t<B, descr<N1 - 1>> const_name(char const (&text1)[N1], char const (&)[N2]) {
|
||||
return const_name(text1);
|
||||
}
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<!B, descr<N2 - 1>> const_name(char const(&)[N1], char const(&text2)[N2]) {
|
||||
constexpr enable_if_t<!B, descr<N2 - 1>> const_name(char const (&)[N1], char const (&text2)[N2]) {
|
||||
return const_name(text2);
|
||||
}
|
||||
|
||||
template <bool B, typename T1, typename T2>
|
||||
constexpr enable_if_t<B, T1> const_name(const T1 &d, const T2 &) { return d; }
|
||||
constexpr enable_if_t<B, T1> const_name(const T1 &d, const T2 &) {
|
||||
return d;
|
||||
}
|
||||
template <bool B, typename T1, typename T2>
|
||||
constexpr enable_if_t<!B, T2> const_name(const T1 &, const T2 &d) { return d; }
|
||||
constexpr enable_if_t<!B, T2> const_name(const T1 &, const T2 &d) {
|
||||
return d;
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
auto constexpr const_name() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
|
||||
return int_to_str<Size / 10, Size % 10>::digits;
|
||||
}
|
||||
|
||||
template <typename Type> constexpr descr<1, Type> const_name() { return {'%'}; }
|
||||
template <typename Type>
|
||||
constexpr descr<1, Type> const_name() {
|
||||
return {'%'};
|
||||
}
|
||||
|
||||
// If "_" is defined as a macro, py::detail::_ cannot be provided.
|
||||
// It is therefore best to use py::detail::const_name universally.
|
||||
// This block is for backward compatibility only.
|
||||
// (The const_name code is repeated to avoid introducing a "_" #define ourselves.)
|
||||
#ifndef _
|
||||
#define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
# define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
template <size_t N>
|
||||
constexpr descr<N-1> _(char const(&text)[N]) { return const_name<N>(text); }
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<B, descr<N1 - 1>> _(char const(&text1)[N1], char const(&text2)[N2]) {
|
||||
return const_name<B,N1,N2>(text1, text2);
|
||||
constexpr descr<N - 1> _(char const (&text)[N]) {
|
||||
return const_name<N>(text);
|
||||
}
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<!B, descr<N2 - 1>> _(char const(&text1)[N1], char const(&text2)[N2]) {
|
||||
return const_name<B,N1,N2>(text1, text2);
|
||||
constexpr enable_if_t<B, descr<N1 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
|
||||
return const_name<B, N1, N2>(text1, text2);
|
||||
}
|
||||
template <bool B, size_t N1, size_t N2>
|
||||
constexpr enable_if_t<!B, descr<N2 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
|
||||
return const_name<B, N1, N2>(text1, text2);
|
||||
}
|
||||
template <bool B, typename T1, typename T2>
|
||||
constexpr enable_if_t<B, T1> _(const T1 &d1, const T2 &d2) { return const_name<B,T1,T2>(d1, d2); }
|
||||
constexpr enable_if_t<B, T1> _(const T1 &d1, const T2 &d2) {
|
||||
return const_name<B, T1, T2>(d1, d2);
|
||||
}
|
||||
template <bool B, typename T1, typename T2>
|
||||
constexpr enable_if_t<!B, T2> _(const T1 &d1, const T2 &d2) { return const_name<B,T1,T2>(d1, d2); }
|
||||
constexpr enable_if_t<!B, T2> _(const T1 &d1, const T2 &d2) {
|
||||
return const_name<B, T1, T2>(d1, d2);
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
|
||||
return const_name<Size>();
|
||||
}
|
||||
template <typename Type> constexpr descr<1, Type> _() { return const_name<Type>(); }
|
||||
#endif // #ifndef _
|
||||
template <typename Type>
|
||||
constexpr descr<1, Type> _() {
|
||||
return const_name<Type>();
|
||||
}
|
||||
#endif // #ifndef _
|
||||
|
||||
constexpr descr<0> concat() { return {}; }
|
||||
|
||||
template <size_t N, typename... Ts>
|
||||
constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) { return descr; }
|
||||
constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) {
|
||||
return descr;
|
||||
}
|
||||
|
||||
template <size_t N, typename... Ts, typename... Args>
|
||||
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
|
||||
|
||||
@ -22,7 +22,8 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename> using cast_op_type = value_and_holder &;
|
||||
template <typename>
|
||||
using cast_op_type = value_and_holder &;
|
||||
explicit operator value_and_holder &() { return *value; }
|
||||
static constexpr auto name = const_name<value_and_holder>();
|
||||
|
||||
@ -39,11 +40,15 @@ inline void no_nullptr(void *ptr) {
|
||||
}
|
||||
|
||||
// Implementing functions for all forms of py::init<...> and py::init(...)
|
||||
template <typename Class> using Cpp = typename Class::type;
|
||||
template <typename Class> using Alias = typename Class::type_alias;
|
||||
template <typename Class> using Holder = typename Class::holder_type;
|
||||
template <typename Class>
|
||||
using Cpp = typename Class::type;
|
||||
template <typename Class>
|
||||
using Alias = typename Class::type_alias;
|
||||
template <typename Class>
|
||||
using Holder = typename Class::holder_type;
|
||||
|
||||
template <typename Class> using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>;
|
||||
template <typename Class>
|
||||
using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>;
|
||||
|
||||
// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance.
|
||||
template <typename Class, enable_if_t<Class::has_alias, int> = 0>
|
||||
@ -52,17 +57,27 @@ bool is_alias(Cpp<Class> *ptr) {
|
||||
}
|
||||
// Failing fallback version of the above for a no-alias class (always returns false)
|
||||
template <typename /*Class*/>
|
||||
constexpr bool is_alias(void *) { return false; }
|
||||
constexpr bool is_alias(void *) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
|
||||
// back to brace aggregate initiailization so that for aggregate initialization can be used with
|
||||
// py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For
|
||||
// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
|
||||
// works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).
|
||||
template <typename Class, typename... Args, detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0>
|
||||
inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward<Args>(args)...); }
|
||||
template <typename Class, typename... Args, detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0>
|
||||
inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward<Args>(args)...}; }
|
||||
template <typename Class,
|
||||
typename... Args,
|
||||
detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0>
|
||||
inline Class *construct_or_initialize(Args &&...args) {
|
||||
return new Class(std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename Class,
|
||||
typename... Args,
|
||||
detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0>
|
||||
inline Class *construct_or_initialize(Args &&...args) {
|
||||
return new Class{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with
|
||||
// an alias to provide only a single Cpp factory function as long as the Alias can be
|
||||
@ -71,12 +86,14 @@ inline Class *construct_or_initialize(Args &&...args) { return new Class{std::fo
|
||||
// inherit all the base class constructors.
|
||||
template <typename Class>
|
||||
void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/,
|
||||
value_and_holder &v_h, Cpp<Class> &&base) {
|
||||
value_and_holder &v_h,
|
||||
Cpp<Class> &&base) {
|
||||
v_h.value_ptr() = new Alias<Class>(std::move(base));
|
||||
}
|
||||
template <typename Class>
|
||||
[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/,
|
||||
value_and_holder &, Cpp<Class> &&) {
|
||||
value_and_holder &,
|
||||
Cpp<Class> &&) {
|
||||
throw type_error("pybind11::init(): unable to convert returned instance to required "
|
||||
"alias class: no `Alias<Class>(Class &&)` constructor available");
|
||||
}
|
||||
@ -86,8 +103,8 @@ template <typename Class>
|
||||
template <typename Class>
|
||||
void construct(...) {
|
||||
static_assert(!std::is_same<Class, Class>::value /* always false */,
|
||||
"pybind11::init(): init function must return a compatible pointer, "
|
||||
"holder, or value");
|
||||
"pybind11::init(): init function must return a compatible pointer, "
|
||||
"holder, or value");
|
||||
}
|
||||
|
||||
// Pointer return v1: the factory function returns a class pointer for a registered class.
|
||||
@ -108,7 +125,7 @@ void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
|
||||
// the holder and destruction happens when we leave the C++ scope, and the holder
|
||||
// class gets to handle the destruction however it likes.
|
||||
v_h.value_ptr() = ptr;
|
||||
v_h.set_instance_registered(true); // To prevent init_instance from registering it
|
||||
v_h.set_instance_registered(true); // To prevent init_instance from registering it
|
||||
v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder
|
||||
Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder
|
||||
v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null
|
||||
@ -131,7 +148,8 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
|
||||
|
||||
// Holder return: copy its pointer, and move or copy the returned holder into the new instance's
|
||||
// holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a
|
||||
// derived type (through those holder's implicit conversion from derived class holder constructors).
|
||||
// derived type (through those holder's implicit conversion from derived class holder
|
||||
// constructors).
|
||||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
@ -155,7 +173,7 @@ template <typename Class>
|
||||
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
static_assert(std::is_move_constructible<Cpp<Class>>::value,
|
||||
"pybind11::init() return-by-value factory function requires a movable class");
|
||||
"pybind11::init() return-by-value factory function requires a movable class");
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias) {
|
||||
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
|
||||
} else {
|
||||
@ -168,7 +186,8 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
||||
// cases where Alias initialization is always desired.
|
||||
template <typename Class>
|
||||
void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
|
||||
static_assert(std::is_move_constructible<Alias<Class>>::value,
|
||||
static_assert(
|
||||
std::is_move_constructible<Alias<Class>>::value,
|
||||
"pybind11::init() return-by-alias-value factory function requires a movable alias class");
|
||||
v_h.value_ptr() = new Alias<Class>(std::move(result));
|
||||
}
|
||||
@ -177,16 +196,21 @@ void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
|
||||
template <typename... Args>
|
||||
struct constructor {
|
||||
template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0>
|
||||
static void execute(Class &cl, const Extra&... extra) {
|
||||
cl.def("__init__", [](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
[](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
|
||||
},
|
||||
is_new_style_constructor(),
|
||||
extra...);
|
||||
}
|
||||
|
||||
template <typename Class, typename... Extra,
|
||||
enable_if_t<Class::has_alias &&
|
||||
std::is_constructible<Cpp<Class>, Args...>::value, int> = 0>
|
||||
static void execute(Class &cl, const Extra&... extra) {
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value,
|
||||
int> = 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
[](value_and_holder &v_h, Args... args) {
|
||||
@ -202,30 +226,46 @@ struct constructor {
|
||||
extra...);
|
||||
}
|
||||
|
||||
template <typename Class, typename... Extra,
|
||||
enable_if_t<Class::has_alias &&
|
||||
!std::is_constructible<Cpp<Class>, Args...>::value, int> = 0>
|
||||
static void execute(Class &cl, const Extra&... extra) {
|
||||
cl.def("__init__", [](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value,
|
||||
int> = 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
[](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr()
|
||||
= construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||
},
|
||||
is_new_style_constructor(),
|
||||
extra...);
|
||||
}
|
||||
};
|
||||
|
||||
// Implementing class for py::init_alias<...>()
|
||||
template <typename... Args> struct alias_constructor {
|
||||
template <typename Class, typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int> = 0>
|
||||
static void execute(Class &cl, const Extra&... extra) {
|
||||
cl.def("__init__", [](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
template <typename... Args>
|
||||
struct alias_constructor {
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value,
|
||||
int> = 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
[](value_and_holder &v_h, Args... args) {
|
||||
v_h.value_ptr()
|
||||
= construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||
},
|
||||
is_new_style_constructor(),
|
||||
extra...);
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation class for py::init(Func) and py::init(Func, AliasFunc)
|
||||
template <typename CFunc, typename AFunc = void_type (*)(),
|
||||
typename = function_signature_t<CFunc>, typename = function_signature_t<AFunc>>
|
||||
template <typename CFunc,
|
||||
typename AFunc = void_type (*)(),
|
||||
typename = function_signature_t<CFunc>,
|
||||
typename = function_signature_t<AFunc>>
|
||||
struct factory;
|
||||
|
||||
// Specialization for py::init(Func)
|
||||
@ -243,22 +283,32 @@ struct factory<Func, void_type (*)(), Return(Args...)> {
|
||||
// instance, or the alias needs to be constructible from a `Class &&` argument.
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra &...extra) && {
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def("__init__", [func = std::move(class_factory)]
|
||||
#else
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def(
|
||||
"__init__",
|
||||
[func = std::move(class_factory)]
|
||||
#else
|
||||
auto &func = class_factory;
|
||||
cl.def("__init__", [func]
|
||||
#endif
|
||||
(value_and_holder &v_h, Args... args) {
|
||||
construct<Class>(v_h, func(std::forward<Args>(args)...),
|
||||
Py_TYPE(v_h.inst) != v_h.type->type);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
cl.def(
|
||||
"__init__",
|
||||
[func]
|
||||
#endif
|
||||
(value_and_holder &v_h, Args... args) {
|
||||
construct<Class>(
|
||||
v_h, func(std::forward<Args>(args)...), Py_TYPE(v_h.inst) != v_h.type->type);
|
||||
},
|
||||
is_new_style_constructor(),
|
||||
extra...);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for py::init(Func, AliasFunc)
|
||||
template <typename CFunc, typename AFunc,
|
||||
typename CReturn, typename... CArgs, typename AReturn, typename... AArgs>
|
||||
template <typename CFunc,
|
||||
typename AFunc,
|
||||
typename CReturn,
|
||||
typename... CArgs,
|
||||
typename AReturn,
|
||||
typename... AArgs>
|
||||
struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
|
||||
static_assert(sizeof...(CArgs) == sizeof...(AArgs),
|
||||
"pybind11::init(class_factory, alias_factory): class and alias factories "
|
||||
@ -271,30 +321,37 @@ struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
|
||||
remove_reference_t<AFunc> alias_factory;
|
||||
|
||||
factory(CFunc &&c, AFunc &&a)
|
||||
: class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) { }
|
||||
: class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) {}
|
||||
|
||||
// The class factory is called when the `self` type passed to `__init__` is the direct
|
||||
// class (i.e. not inherited), the alias factory when `self` is a Python-side subtype.
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra&... extra) && {
|
||||
static_assert(Class::has_alias, "The two-argument version of `py::init()` can "
|
||||
"only be used if the class has an alias");
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
|
||||
#else
|
||||
void execute(Class &cl, const Extra &...extra) && {
|
||||
static_assert(Class::has_alias,
|
||||
"The two-argument version of `py::init()` can "
|
||||
"only be used if the class has an alias");
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def(
|
||||
"__init__",
|
||||
[class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
|
||||
#else
|
||||
auto &class_func = class_factory;
|
||||
auto &alias_func = alias_factory;
|
||||
cl.def("__init__", [class_func, alias_func]
|
||||
#endif
|
||||
(value_and_holder &v_h, CArgs... args) {
|
||||
if (Py_TYPE(v_h.inst) == v_h.type->type) {
|
||||
// If the instance type equals the registered type we don't have inheritance, so
|
||||
// don't need the alias and can construct using the class function:
|
||||
construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
|
||||
} else {
|
||||
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
|
||||
}
|
||||
}, is_new_style_constructor(), extra...);
|
||||
cl.def(
|
||||
"__init__",
|
||||
[class_func, alias_func]
|
||||
#endif
|
||||
(value_and_holder &v_h, CArgs... args) {
|
||||
if (Py_TYPE(v_h.inst) == v_h.type->type) {
|
||||
// If the instance type equals the registered type we don't have inheritance,
|
||||
// so don't need the alias and can construct using the class function:
|
||||
construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
|
||||
} else {
|
||||
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
|
||||
}
|
||||
},
|
||||
is_new_style_constructor(),
|
||||
extra...);
|
||||
}
|
||||
};
|
||||
|
||||
@ -305,7 +362,9 @@ void setstate(value_and_holder &v_h, T &&result, bool need_alias) {
|
||||
}
|
||||
|
||||
/// Set both the C++ and Python states
|
||||
template <typename Class, typename T, typename O,
|
||||
template <typename Class,
|
||||
typename T,
|
||||
typename O,
|
||||
enable_if_t<std::is_convertible<O, handle>::value, int> = 0>
|
||||
void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) {
|
||||
construct<Class>(v_h, std::move(result.first), need_alias);
|
||||
@ -319,12 +378,18 @@ void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias)
|
||||
}
|
||||
|
||||
/// Implementation for py::pickle(GetState, SetState)
|
||||
template <typename Get, typename Set,
|
||||
typename = function_signature_t<Get>, typename = function_signature_t<Set>>
|
||||
template <typename Get,
|
||||
typename Set,
|
||||
typename = function_signature_t<Get>,
|
||||
typename = function_signature_t<Set>>
|
||||
struct pickle_factory;
|
||||
|
||||
template <typename Get, typename Set,
|
||||
typename RetState, typename Self, typename NewInstance, typename ArgState>
|
||||
template <typename Get,
|
||||
typename Set,
|
||||
typename RetState,
|
||||
typename Self,
|
||||
typename NewInstance,
|
||||
typename ArgState>
|
||||
struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
|
||||
static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value,
|
||||
"The type returned by `__getstate__` must be the same "
|
||||
@ -333,23 +398,28 @@ struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
|
||||
remove_reference_t<Get> get;
|
||||
remove_reference_t<Set> set;
|
||||
|
||||
pickle_factory(Get get, Set set)
|
||||
: get(std::forward<Get>(get)), set(std::forward<Set>(set)) { }
|
||||
pickle_factory(Get get, Set set) : get(std::forward<Get>(get)), set(std::forward<Set>(set)) {}
|
||||
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra &...extra) && {
|
||||
cl.def("__getstate__", std::move(get));
|
||||
|
||||
#if defined(PYBIND11_CPP14)
|
||||
cl.def("__setstate__", [func = std::move(set)]
|
||||
cl.def(
|
||||
"__setstate__",
|
||||
[func = std::move(set)]
|
||||
#else
|
||||
auto &func = set;
|
||||
cl.def("__setstate__", [func]
|
||||
cl.def(
|
||||
"__setstate__",
|
||||
[func]
|
||||
#endif
|
||||
(value_and_holder &v_h, ArgState state) {
|
||||
setstate<Class>(v_h, func(std::forward<ArgState>(state)),
|
||||
Py_TYPE(v_h.inst) != v_h.type->type);
|
||||
}, is_new_style_constructor(), extra...);
|
||||
(value_and_holder &v_h, ArgState state) {
|
||||
setstate<Class>(
|
||||
v_h, func(std::forward<ArgState>(state)), Py_TYPE(v_h.inst) != v_h.type->type);
|
||||
},
|
||||
is_new_style_constructor(),
|
||||
extra...);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../pytypes.h"
|
||||
|
||||
#include <exception>
|
||||
|
||||
/// Tracks the `internals` and `type_info` ABI version independent of the main library version.
|
||||
@ -136,9 +137,9 @@ template <typename value_type>
|
||||
using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>;
|
||||
|
||||
struct override_hash {
|
||||
inline size_t operator()(const std::pair<const PyObject *, const char *>& v) const {
|
||||
inline size_t operator()(const std::pair<const PyObject *, const char *> &v) const {
|
||||
size_t value = std::hash<const void *>()(v.first);
|
||||
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2);
|
||||
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value << 6) + (value >> 2);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
@ -151,8 +152,9 @@ struct internals {
|
||||
type_map<type_info *> registered_types_cpp;
|
||||
// PyTypeObject* -> base type_info(s)
|
||||
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py;
|
||||
std::unordered_multimap<const void *, instance*> registered_instances; // void * -> instance*
|
||||
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash> inactive_override_cache;
|
||||
std::unordered_multimap<const void *, instance *> registered_instances; // void * -> instance*
|
||||
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash>
|
||||
inactive_override_cache;
|
||||
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
|
||||
std::unordered_map<const PyObject *, std::vector<PyObject *>> patients;
|
||||
std::forward_list<ExceptionTranslator> registered_exception_translators;
|
||||
@ -198,8 +200,8 @@ struct type_info {
|
||||
void *(*operator_new)(size_t);
|
||||
void (*init_instance)(instance *, const void *);
|
||||
void (*dealloc)(value_and_holder &v_h);
|
||||
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
|
||||
std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts;
|
||||
std::vector<PyObject *(*) (PyObject *, PyTypeObject *)> implicit_conversions;
|
||||
std::vector<std::pair<const std::type_info *, void *(*) (void *)>> implicit_casts;
|
||||
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;
|
||||
buffer_info *(*get_buffer)(PyObject *, void *) = nullptr;
|
||||
void *get_buffer_data = nullptr;
|
||||
@ -219,67 +221,71 @@ struct type_info {
|
||||
|
||||
/// On MSVC, debug and release builds are not ABI-compatible!
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
# define PYBIND11_BUILD_TYPE "_debug"
|
||||
#else
|
||||
# define PYBIND11_BUILD_TYPE ""
|
||||
# define PYBIND11_BUILD_TYPE ""
|
||||
#endif
|
||||
|
||||
/// Let's assume that different compilers are ABI-incompatible.
|
||||
/// A user can manually set this string if they know their
|
||||
/// compiler is compatible.
|
||||
#ifndef PYBIND11_COMPILER_TYPE
|
||||
# if defined(_MSC_VER)
|
||||
# define PYBIND11_COMPILER_TYPE "_msvc"
|
||||
# elif defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_COMPILER_TYPE "_icc"
|
||||
# elif defined(__clang__)
|
||||
# define PYBIND11_COMPILER_TYPE "_clang"
|
||||
# elif defined(__PGI)
|
||||
# define PYBIND11_COMPILER_TYPE "_pgi"
|
||||
# elif defined(__MINGW32__)
|
||||
# define PYBIND11_COMPILER_TYPE "_mingw"
|
||||
# elif defined(__CYGWIN__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
|
||||
# elif defined(__GNUC__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc"
|
||||
# else
|
||||
# define PYBIND11_COMPILER_TYPE "_unknown"
|
||||
# endif
|
||||
# if defined(_MSC_VER)
|
||||
# define PYBIND11_COMPILER_TYPE "_msvc"
|
||||
# elif defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_COMPILER_TYPE "_icc"
|
||||
# elif defined(__clang__)
|
||||
# define PYBIND11_COMPILER_TYPE "_clang"
|
||||
# elif defined(__PGI)
|
||||
# define PYBIND11_COMPILER_TYPE "_pgi"
|
||||
# elif defined(__MINGW32__)
|
||||
# define PYBIND11_COMPILER_TYPE "_mingw"
|
||||
# elif defined(__CYGWIN__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
|
||||
# elif defined(__GNUC__)
|
||||
# define PYBIND11_COMPILER_TYPE "_gcc"
|
||||
# else
|
||||
# define PYBIND11_COMPILER_TYPE "_unknown"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/// Also standard libs
|
||||
#ifndef PYBIND11_STDLIB
|
||||
# if defined(_LIBCPP_VERSION)
|
||||
# define PYBIND11_STDLIB "_libcpp"
|
||||
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||
# define PYBIND11_STDLIB "_libstdcpp"
|
||||
# else
|
||||
# define PYBIND11_STDLIB ""
|
||||
# endif
|
||||
# if defined(_LIBCPP_VERSION)
|
||||
# define PYBIND11_STDLIB "_libcpp"
|
||||
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||
# define PYBIND11_STDLIB "_libstdcpp"
|
||||
# else
|
||||
# define PYBIND11_STDLIB ""
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
|
||||
#ifndef PYBIND11_BUILD_ABI
|
||||
# if defined(__GXX_ABI_VERSION)
|
||||
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
|
||||
# else
|
||||
# define PYBIND11_BUILD_ABI ""
|
||||
# endif
|
||||
# if defined(__GXX_ABI_VERSION)
|
||||
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
|
||||
# else
|
||||
# define PYBIND11_BUILD_ABI ""
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PYBIND11_INTERNALS_KIND
|
||||
# if defined(WITH_THREAD)
|
||||
# define PYBIND11_INTERNALS_KIND ""
|
||||
# else
|
||||
# define PYBIND11_INTERNALS_KIND "_without_thread"
|
||||
# endif
|
||||
# if defined(WITH_THREAD)
|
||||
# define PYBIND11_INTERNALS_KIND ""
|
||||
# else
|
||||
# define PYBIND11_INTERNALS_KIND "_without_thread"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \
|
||||
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
|
||||
#define PYBIND11_INTERNALS_ID \
|
||||
"__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
||||
PYBIND11_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_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_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`.
|
||||
@ -396,8 +402,12 @@ inline void translate_local_exception(std::exception_ptr p) {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (error_already_set &e) { e.restore(); return;
|
||||
} catch (const builtin_exception &e) { e.set_error(); return;
|
||||
} catch (error_already_set &e) {
|
||||
e.restore();
|
||||
return;
|
||||
} catch (const builtin_exception &e) {
|
||||
e.set_error();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -412,7 +422,7 @@ PYBIND11_NOINLINE internals &get_internals() {
|
||||
// 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() : state(PyGILState_Ensure()) {}
|
||||
~gil_scoped_acquire_local() { PyGILState_Release(state); }
|
||||
const PyGILState_STATE state;
|
||||
} gil;
|
||||
@ -512,11 +522,10 @@ struct local_internals {
|
||||
|
||||
/// Works like `get_internals`, but for things which are locally registered.
|
||||
inline local_internals &get_local_internals() {
|
||||
static local_internals locals;
|
||||
return locals;
|
||||
static local_internals locals;
|
||||
return locals;
|
||||
}
|
||||
|
||||
|
||||
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
|
||||
/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only
|
||||
/// cleared when the program exits or after interpreter shutdown (when embedding), and so are
|
||||
@ -548,7 +557,7 @@ PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
|
||||
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if
|
||||
/// such entry exists. Otherwise, a new object of default-constructible type `T` is
|
||||
/// added to the shared data under the given name and a reference to it is returned.
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
T &get_or_create_shared_data(const std::string &name) {
|
||||
auto &internals = detail::get_internals();
|
||||
auto it = internals.shared_data.find(name);
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include "descr.h"
|
||||
#include "internals.h"
|
||||
#include "typeid.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
@ -32,7 +33,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
/// Adding a patient will keep it alive up until the enclosing function returns.
|
||||
class loader_life_support {
|
||||
private:
|
||||
loader_life_support* parent = nullptr;
|
||||
loader_life_support *parent = nullptr;
|
||||
std::unordered_set<PyObject *> keep_alive;
|
||||
|
||||
#if defined(WITH_THREAD)
|
||||
@ -98,7 +99,8 @@ public:
|
||||
// Gets the cache entry for the given type, creating it if necessary. The return value is the pair
|
||||
// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was
|
||||
// just created.
|
||||
inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_type_info_get_cache(PyTypeObject *type);
|
||||
inline std::pair<decltype(internals::registered_types_py)::iterator, bool>
|
||||
all_type_info_get_cache(PyTypeObject *type);
|
||||
|
||||
// Populates a just-created cache entry.
|
||||
PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) {
|
||||
@ -119,23 +121,25 @@ PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_
|
||||
auto it = type_dict.find(type);
|
||||
if (it != type_dict.end()) {
|
||||
// We found a cache entry for it, so it's either pybind-registered or has pre-computed
|
||||
// pybind bases, but we have to make sure we haven't already seen the type(s) before: we
|
||||
// want to follow Python/virtual C++ rules that there should only be one instance of a
|
||||
// common base.
|
||||
// pybind bases, but we have to make sure we haven't already seen the type(s) before:
|
||||
// we want to follow Python/virtual C++ rules that there should only be one instance of
|
||||
// a common base.
|
||||
for (auto *tinfo : it->second) {
|
||||
// NB: Could use a second set here, rather than doing a linear search, but since
|
||||
// having a large number of immediate pybind11-registered types seems fairly
|
||||
// unlikely, that probably isn't worthwhile.
|
||||
bool found = false;
|
||||
for (auto *known : bases) {
|
||||
if (known == tinfo) { found = true; break; }
|
||||
if (known == tinfo) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
bases.push_back(tinfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type->tp_bases) {
|
||||
} else if (type->tp_bases) {
|
||||
// It's some python type, so keep follow its bases classes to look for one or more
|
||||
// registered types
|
||||
if (i + 1 == check.size()) {
|
||||
@ -177,7 +181,7 @@ inline const std::vector<detail::type_info *> &all_type_info(PyTypeObject *type)
|
||||
* ancestors are pybind11-registered. Throws an exception if there are multiple bases--use
|
||||
* `all_type_info` instead if you want to support multiple bases.
|
||||
*/
|
||||
PYBIND11_NOINLINE detail::type_info* get_type_info(PyTypeObject *type) {
|
||||
PYBIND11_NOINLINE detail::type_info *get_type_info(PyTypeObject *type) {
|
||||
const auto &bases = all_type_info(type);
|
||||
if (bases.empty()) {
|
||||
return nullptr;
|
||||
@ -207,9 +211,10 @@ inline detail::type_info *get_global_type_info(const std::type_index &tp) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr.
|
||||
/// Return the type info for a given C++ type; on lookup failure can either throw or return
|
||||
/// nullptr.
|
||||
PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_index &tp,
|
||||
bool throw_if_missing = false) {
|
||||
bool throw_if_missing = false) {
|
||||
if (auto *ltype = get_local_type_info(tp)) {
|
||||
return ltype;
|
||||
}
|
||||
@ -220,7 +225,8 @@ PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_index &tp,
|
||||
if (throw_if_missing) {
|
||||
std::string tname = tp.name();
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\"");
|
||||
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname
|
||||
+ "\"");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -232,7 +238,7 @@ PYBIND11_NOINLINE handle get_type_handle(const std::type_info &tp, bool throw_if
|
||||
|
||||
// Searches the inheritance graph for a registered Python instance, using all_type_info().
|
||||
PYBIND11_NOINLINE handle find_registered_python_instance(void *src,
|
||||
const detail::type_info *tinfo) {
|
||||
const detail::type_info *tinfo) {
|
||||
auto it_instances = get_internals().registered_instances.equal_range(src);
|
||||
for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
|
||||
for (auto *instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
|
||||
@ -251,10 +257,10 @@ struct value_and_holder {
|
||||
void **vh = nullptr;
|
||||
|
||||
// Main constructor for a found value/holder:
|
||||
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) :
|
||||
inst{i}, index{index}, type{type},
|
||||
vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]}
|
||||
{}
|
||||
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index)
|
||||
: inst{i}, index{index}, type{type}, vh{inst->simple_layout
|
||||
? inst->simple_value_holder
|
||||
: &inst->nonsimple.values_and_holders[vpos]} {}
|
||||
|
||||
// Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
|
||||
value_and_holder() = default;
|
||||
@ -262,13 +268,15 @@ struct value_and_holder {
|
||||
// Used for past-the-end iterator
|
||||
explicit value_and_holder(size_t index) : index{index} {}
|
||||
|
||||
template <typename V = void> V *&value_ptr() const {
|
||||
template <typename V = void>
|
||||
V *&value_ptr() const {
|
||||
return reinterpret_cast<V *&>(vh[0]);
|
||||
}
|
||||
// True if this `value_and_holder` has a non-null value pointer
|
||||
explicit operator bool() const { return value_ptr() != nullptr; }
|
||||
|
||||
template <typename H> H &holder() const {
|
||||
template <typename H>
|
||||
H &holder() const {
|
||||
return reinterpret_cast<H &>(vh[1]);
|
||||
}
|
||||
bool holder_constructed() const {
|
||||
@ -288,8 +296,8 @@ struct value_and_holder {
|
||||
}
|
||||
bool instance_registered() const {
|
||||
return inst->simple_layout
|
||||
? inst->simple_instance_registered
|
||||
: ((inst->nonsimple.status[index] & instance::status_instance_registered) != 0);
|
||||
? inst->simple_instance_registered
|
||||
: ((inst->nonsimple.status[index] & instance::status_instance_registered) != 0);
|
||||
}
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
void set_instance_registered(bool v = true) {
|
||||
@ -322,11 +330,10 @@ public:
|
||||
friend struct values_and_holders;
|
||||
iterator(instance *inst, const type_vec *tinfo)
|
||||
: inst{inst}, types{tinfo},
|
||||
curr(inst /* instance */,
|
||||
types->empty() ? nullptr : (*types)[0] /* type info */,
|
||||
0, /* vpos: (non-simple types only): the first vptr comes first */
|
||||
0 /* index */)
|
||||
{}
|
||||
curr(inst /* instance */,
|
||||
types->empty() ? nullptr : (*types)[0] /* type info */,
|
||||
0, /* vpos: (non-simple types only): the first vptr comes first */
|
||||
0 /* index */) {}
|
||||
// Past-the-end iterator:
|
||||
explicit iterator(size_t end) : curr(end) {}
|
||||
|
||||
@ -369,7 +376,9 @@ public:
|
||||
* The returned object should be short-lived: in particular, it must not outlive the called-upon
|
||||
* instance.
|
||||
*/
|
||||
PYBIND11_NOINLINE value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) {
|
||||
PYBIND11_NOINLINE value_and_holder
|
||||
instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/,
|
||||
bool throw_if_missing /*= true in common.h*/) {
|
||||
// Optimize common case:
|
||||
if (!find_type || Py_TYPE(this) == find_type->type) {
|
||||
return value_and_holder(this, find_type, 0, 0);
|
||||
@ -387,12 +396,13 @@ PYBIND11_NOINLINE value_and_holder instance::get_value_and_holder(const type_inf
|
||||
|
||||
#if defined(NDEBUG)
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: "
|
||||
"type is not a pybind11 base of the given instance "
|
||||
"(compile in debug mode for type details)");
|
||||
"type is not a pybind11 base of the given instance "
|
||||
"(compile in debug mode for type details)");
|
||||
#else
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" +
|
||||
get_fully_qualified_tp_name(find_type->type) + "' is not a pybind11 base of the given `" +
|
||||
get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance");
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `"
|
||||
+ get_fully_qualified_tp_name(find_type->type)
|
||||
+ "' is not a pybind11 base of the given `"
|
||||
+ get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -406,23 +416,22 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
|
||||
"instance allocation failed: new instance has no pybind11-registered base types");
|
||||
}
|
||||
|
||||
simple_layout =
|
||||
n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs();
|
||||
simple_layout
|
||||
= n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs();
|
||||
|
||||
// Simple path: no python-side multiple inheritance, and a small-enough holder
|
||||
if (simple_layout) {
|
||||
simple_value_holder[0] = nullptr;
|
||||
simple_holder_constructed = false;
|
||||
simple_instance_registered = false;
|
||||
}
|
||||
else { // multiple base types or a too-large holder
|
||||
} else { // multiple base types or a too-large holder
|
||||
// Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer,
|
||||
// [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool
|
||||
// values that tracks whether each associated holder has been initialized. Each [block] is
|
||||
// padded, if necessary, to an integer multiple of sizeof(void *).
|
||||
size_t space = 0;
|
||||
for (auto *t : tinfo) {
|
||||
space += 1; // value pointer
|
||||
space += 1; // value pointer
|
||||
space += t->holder_size_in_ptrs; // holder instance
|
||||
}
|
||||
size_t flags_at = space;
|
||||
@ -441,10 +450,12 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
|
||||
}
|
||||
#else
|
||||
nonsimple.values_and_holders = (void **) PyMem_New(void *, space);
|
||||
if (!nonsimple.values_and_holders) throw std::bad_alloc();
|
||||
if (!nonsimple.values_and_holders)
|
||||
throw std::bad_alloc();
|
||||
std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *));
|
||||
#endif
|
||||
nonsimple.status = reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]);
|
||||
nonsimple.status
|
||||
= reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]);
|
||||
}
|
||||
owned = true;
|
||||
}
|
||||
@ -501,17 +512,16 @@ PYBIND11_NOINLINE std::string error_string() {
|
||||
PyFrameObject *frame = trace->tb_frame;
|
||||
errorString += "\n\nAt:\n";
|
||||
while (frame) {
|
||||
#if PY_VERSION_HEX >= 0x03090000
|
||||
# if PY_VERSION_HEX >= 0x03090000
|
||||
PyCodeObject *f_code = PyFrame_GetCode(frame);
|
||||
#else
|
||||
# else
|
||||
PyCodeObject *f_code = frame->f_code;
|
||||
Py_INCREF(f_code);
|
||||
#endif
|
||||
# endif
|
||||
int lineno = PyFrame_GetLineNumber(frame);
|
||||
errorString +=
|
||||
" " + handle(f_code->co_filename).cast<std::string>() +
|
||||
"(" + std::to_string(lineno) + "): " +
|
||||
handle(f_code->co_name).cast<std::string>() + "\n";
|
||||
errorString += " " + handle(f_code->co_filename).cast<std::string>() + "("
|
||||
+ std::to_string(lineno)
|
||||
+ "): " + handle(f_code->co_name).cast<std::string>() + "\n";
|
||||
frame = frame->f_back;
|
||||
Py_DECREF(f_code);
|
||||
}
|
||||
@ -521,7 +531,7 @@ PYBIND11_NOINLINE std::string error_string() {
|
||||
return errorString;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type ) {
|
||||
PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type) {
|
||||
auto &instances = get_internals().registered_instances;
|
||||
auto range = instances.equal_range(ptr);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
@ -540,9 +550,9 @@ inline PyThreadState *get_thread_state_unchecked() {
|
||||
#elif PY_VERSION_HEX < 0x03000000
|
||||
return _PyThreadState_Current;
|
||||
#elif PY_VERSION_HEX < 0x03050000
|
||||
return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||
return (PyThreadState *) _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||
#elif PY_VERSION_HEX < 0x03050200
|
||||
return (PyThreadState*) _PyThreadState_Current.value;
|
||||
return (PyThreadState *) _PyThreadState_Current.value;
|
||||
#else
|
||||
return _PyThreadState_UncheckedGet();
|
||||
#endif
|
||||
@ -560,11 +570,11 @@ public:
|
||||
explicit type_caster_generic(const type_info *typeinfo)
|
||||
: typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) {}
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
return load_impl<type_caster_generic>(src, convert);
|
||||
}
|
||||
bool load(handle src, bool convert) { return load_impl<type_caster_generic>(src, convert); }
|
||||
|
||||
PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent,
|
||||
PYBIND11_NOINLINE static handle cast(const void *_src,
|
||||
return_value_policy policy,
|
||||
handle parent,
|
||||
const detail::type_info *tinfo,
|
||||
void *(*copy_constructor)(const void *),
|
||||
void *(*move_constructor)(const void *),
|
||||
@ -610,8 +620,8 @@ public:
|
||||
#else
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = copy, but type " +
|
||||
type_name + " is non-copyable!");
|
||||
throw cast_error("return_value_policy = copy, but type " + type_name
|
||||
+ " is non-copyable!");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
@ -630,8 +640,8 @@ public:
|
||||
#else
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = move, but type " +
|
||||
type_name + " is neither movable nor copyable!");
|
||||
throw cast_error("return_value_policy = move, but type " + type_name
|
||||
+ " is neither movable nor copyable!");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
@ -661,16 +671,15 @@ public:
|
||||
if (type->operator_new) {
|
||||
vptr = type->operator_new(type->type_size);
|
||||
} else {
|
||||
#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
|
||||
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
|
||||
vptr = ::operator new(type->type_size,
|
||||
std::align_val_t(type->type_align));
|
||||
} else {
|
||||
vptr = ::operator new(type->type_size);
|
||||
}
|
||||
#else
|
||||
#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
|
||||
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
|
||||
vptr = ::operator new(type->type_size, std::align_val_t(type->type_align));
|
||||
} else {
|
||||
vptr = ::operator new(type->type_size);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
vptr = ::operator new(type->type_size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
value = vptr;
|
||||
@ -713,7 +722,8 @@ public:
|
||||
}
|
||||
|
||||
type_info *foreign_typeinfo = reinterpret_borrow<capsule>(getattr(pytype, local_key));
|
||||
// Only consider this foreign loader if actually foreign and is a loader of the correct cpp type
|
||||
// Only consider this foreign loader if actually foreign and is a loader of the correct cpp
|
||||
// type
|
||||
if (foreign_typeinfo->module_local_load == &local_load
|
||||
|| (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) {
|
||||
return false;
|
||||
@ -764,21 +774,23 @@ public:
|
||||
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
|
||||
return true;
|
||||
}
|
||||
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if
|
||||
// we can find an exact match (or, for a simple C++ type, an inherited match); if so, we
|
||||
// can safely reinterpret_cast to the relevant pointer.
|
||||
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see
|
||||
// if we can find an exact match (or, for a simple C++ type, an inherited match); if
|
||||
// so, we can safely reinterpret_cast to the relevant pointer.
|
||||
if (bases.size() > 1) {
|
||||
for (auto *base : bases) {
|
||||
if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) {
|
||||
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base));
|
||||
if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type)
|
||||
: base->type == typeinfo->type) {
|
||||
this_.load_value(
|
||||
reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match
|
||||
// in the registered bases, above, so try implicit casting (needed for proper C++ casting
|
||||
// when MI is involved).
|
||||
// Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type
|
||||
// match in the registered bases, above, so try implicit casting (needed for proper C++
|
||||
// casting when MI is involved).
|
||||
if (this_.try_implicit_casts(src, convert)) {
|
||||
return true;
|
||||
}
|
||||
@ -808,28 +820,29 @@ public:
|
||||
|
||||
// Global typeinfo has precedence over foreign module_local
|
||||
if (try_load_foreign_module_local(src)) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Custom converters didn't take None, now we convert None to nullptr.
|
||||
if (src.is_none()) {
|
||||
// Defer accepting None to other overloads (if we aren't in convert mode):
|
||||
if (!convert) {
|
||||
return false;
|
||||
}
|
||||
value = nullptr;
|
||||
return true;
|
||||
// Defer accepting None to other overloads (if we aren't in convert mode):
|
||||
if (!convert) {
|
||||
return false;
|
||||
}
|
||||
value = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast
|
||||
// isn't needed or can't be used. If the type is unknown, sets the error and returns a pair
|
||||
// with .second = nullptr. (p.first = nullptr is not an error: it becomes None).
|
||||
PYBIND11_NOINLINE static std::pair<const void *, const type_info *> src_and_type(
|
||||
const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) {
|
||||
PYBIND11_NOINLINE static std::pair<const void *, const type_info *>
|
||||
src_and_type(const void *src,
|
||||
const std::type_info &cast_type,
|
||||
const std::type_info *rtti_type = nullptr) {
|
||||
if (auto *tpi = get_type_info(cast_type)) {
|
||||
return {src, const_cast<const type_info *>(tpi)};
|
||||
}
|
||||
@ -855,10 +868,9 @@ public:
|
||||
* `movable_cast_op_type` instead.
|
||||
*/
|
||||
template <typename T>
|
||||
using cast_op_type =
|
||||
conditional_t<std::is_pointer<remove_reference_t<T>>::value,
|
||||
typename std::add_pointer<intrinsic_t<T>>::type,
|
||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>;
|
||||
using cast_op_type = conditional_t<std::is_pointer<remove_reference_t<T>>::value,
|
||||
typename std::add_pointer<intrinsic_t<T>>::type,
|
||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>;
|
||||
|
||||
/**
|
||||
* Determine suitable casting operator for a type caster with a movable value. Such a type caster
|
||||
@ -868,40 +880,50 @@ using cast_op_type =
|
||||
* These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro.
|
||||
*/
|
||||
template <typename T>
|
||||
using movable_cast_op_type =
|
||||
conditional_t<std::is_pointer<typename std::remove_reference<T>::type>::value,
|
||||
typename std::add_pointer<intrinsic_t<T>>::type,
|
||||
conditional_t<std::is_rvalue_reference<T>::value,
|
||||
typename std::add_rvalue_reference<intrinsic_t<T>>::type,
|
||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>>;
|
||||
using movable_cast_op_type
|
||||
= conditional_t<std::is_pointer<typename std::remove_reference<T>::type>::value,
|
||||
typename std::add_pointer<intrinsic_t<T>>::type,
|
||||
conditional_t<std::is_rvalue_reference<T>::value,
|
||||
typename std::add_rvalue_reference<intrinsic_t<T>>::type,
|
||||
typename std::add_lvalue_reference<intrinsic_t<T>>::type>>;
|
||||
|
||||
// std::is_copy_constructible isn't quite enough: it lets std::vector<T> (and similar) through when
|
||||
// T is non-copyable, but code containing such a copy constructor fails to actually compile.
|
||||
template <typename T, typename SFINAE = void> struct is_copy_constructible : std::is_copy_constructible<T> {};
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct is_copy_constructible : std::is_copy_constructible<T> {};
|
||||
|
||||
// Specialization for types that appear to be copy constructible but also look like stl containers
|
||||
// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if
|
||||
// so, copy constructability depends on whether the value_type is copy constructible.
|
||||
template <typename Container> struct is_copy_constructible<Container, enable_if_t<all_of<
|
||||
std::is_copy_constructible<Container>,
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>,
|
||||
// Avoid infinite recursion
|
||||
negation<std::is_same<Container, typename Container::value_type>>
|
||||
>::value>> : is_copy_constructible<typename Container::value_type> {};
|
||||
template <typename Container>
|
||||
struct is_copy_constructible<
|
||||
Container,
|
||||
enable_if_t<
|
||||
all_of<std::is_copy_constructible<Container>,
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>,
|
||||
// Avoid infinite recursion
|
||||
negation<std::is_same<Container, typename Container::value_type>>>::value>>
|
||||
: is_copy_constructible<typename Container::value_type> {};
|
||||
|
||||
// Likewise for std::pair
|
||||
// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves
|
||||
// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers).
|
||||
template <typename T1, typename T2> struct is_copy_constructible<std::pair<T1, T2>>
|
||||
// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't
|
||||
// themselves copy constructible, but this can not be relied upon when T1 or T2 are themselves
|
||||
// containers).
|
||||
template <typename T1, typename T2>
|
||||
struct is_copy_constructible<std::pair<T1, T2>>
|
||||
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
|
||||
|
||||
// The same problems arise with std::is_copy_assignable, so we use the same workaround.
|
||||
template <typename T, typename SFINAE = void> struct is_copy_assignable : std::is_copy_assignable<T> {};
|
||||
template <typename Container> struct is_copy_assignable<Container, enable_if_t<all_of<
|
||||
std::is_copy_assignable<Container>,
|
||||
std::is_same<typename Container::value_type &, typename Container::reference>
|
||||
>::value>> : is_copy_assignable<typename Container::value_type> {};
|
||||
template <typename T1, typename T2> struct is_copy_assignable<std::pair<T1, T2>>
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct is_copy_assignable : std::is_copy_assignable<T> {};
|
||||
template <typename Container>
|
||||
struct is_copy_assignable<Container,
|
||||
enable_if_t<all_of<std::is_copy_assignable<Container>,
|
||||
std::is_same<typename Container::value_type &,
|
||||
typename Container::reference>>::value>>
|
||||
: is_copy_assignable<typename Container::value_type> {};
|
||||
template <typename T1, typename T2>
|
||||
struct is_copy_assignable<std::pair<T1, T2>>
|
||||
: all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
@ -928,16 +950,14 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
// std::enable_if. User provided specializations will always have higher priority than
|
||||
// the default implementation and specialization provided in polymorphic_type_hook_base.
|
||||
template <typename itype, typename SFINAE = void>
|
||||
struct polymorphic_type_hook_base
|
||||
{
|
||||
static const void *get(const itype *src, const std::type_info*&) { return src; }
|
||||
struct polymorphic_type_hook_base {
|
||||
static const void *get(const itype *src, const std::type_info *&) { return src; }
|
||||
};
|
||||
template <typename itype>
|
||||
struct polymorphic_type_hook_base<itype, detail::enable_if_t<std::is_polymorphic<itype>::value>>
|
||||
{
|
||||
static const void *get(const itype *src, const std::type_info*& type) {
|
||||
struct polymorphic_type_hook_base<itype, detail::enable_if_t<std::is_polymorphic<itype>::value>> {
|
||||
static const void *get(const itype *src, const std::type_info *&type) {
|
||||
type = src ? &typeid(*src) : nullptr;
|
||||
return dynamic_cast<const void*>(src);
|
||||
return dynamic_cast<const void *>(src);
|
||||
}
|
||||
};
|
||||
template <typename itype, typename SFINAE = void>
|
||||
@ -946,14 +966,15 @@ struct polymorphic_type_hook : public polymorphic_type_hook_base<itype> {};
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Generic type caster for objects stored on the heap
|
||||
template <typename type> class type_caster_base : public type_caster_generic {
|
||||
template <typename type>
|
||||
class type_caster_base : public type_caster_generic {
|
||||
using itype = intrinsic_t<type>;
|
||||
|
||||
public:
|
||||
static constexpr auto name = const_name<type>();
|
||||
|
||||
type_caster_base() : type_caster_base(typeid(type)) { }
|
||||
explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { }
|
||||
type_caster_base() : type_caster_base(typeid(type)) {}
|
||||
explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) {}
|
||||
|
||||
static handle cast(const itype &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
@ -987,31 +1008,39 @@ public:
|
||||
return {vsrc, tpi};
|
||||
}
|
||||
}
|
||||
// Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so
|
||||
// don't do a cast
|
||||
// Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer,
|
||||
// so don't do a cast
|
||||
return type_caster_generic::src_and_type(src, cast_type, instance_type);
|
||||
}
|
||||
|
||||
static handle cast(const itype *src, return_value_policy policy, handle parent) {
|
||||
auto st = src_and_type(src);
|
||||
return type_caster_generic::cast(
|
||||
st.first, policy, parent, st.second,
|
||||
make_copy_constructor(src), make_move_constructor(src));
|
||||
return type_caster_generic::cast(st.first,
|
||||
policy,
|
||||
parent,
|
||||
st.second,
|
||||
make_copy_constructor(src),
|
||||
make_move_constructor(src));
|
||||
}
|
||||
|
||||
static handle cast_holder(const itype *src, const void *holder) {
|
||||
auto st = src_and_type(src);
|
||||
return type_caster_generic::cast(
|
||||
st.first, return_value_policy::take_ownership, {}, st.second,
|
||||
nullptr, nullptr, holder);
|
||||
return type_caster_generic::cast(st.first,
|
||||
return_value_policy::take_ownership,
|
||||
{},
|
||||
st.second,
|
||||
nullptr,
|
||||
nullptr,
|
||||
holder);
|
||||
}
|
||||
|
||||
template <typename T> using cast_op_type = detail::cast_op_type<T>;
|
||||
template <typename T>
|
||||
using cast_op_type = detail::cast_op_type<T>;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator itype*() { return (type *) value; }
|
||||
operator itype *() { return (type *) value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator itype&() {
|
||||
operator itype &() {
|
||||
if (!value) {
|
||||
throw reference_cast_error();
|
||||
}
|
||||
@ -1019,20 +1048,20 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
using Constructor = void *(*)(const void *);
|
||||
using Constructor = void *(*) (const void *);
|
||||
|
||||
/* Only enabled when the types are {copy,move}-constructible *and* when the type
|
||||
does not have a private operator new implementation. A comma operator is used in the decltype
|
||||
argument to apply SFINAE to the public copy/move constructors.*/
|
||||
does not have a private operator new implementation. A comma operator is used in the
|
||||
decltype argument to apply SFINAE to the public copy/move constructors.*/
|
||||
template <typename T, typename = enable_if_t<is_copy_constructible<T>::value>>
|
||||
static auto make_copy_constructor(const T *) -> decltype(new T(std::declval<const T>()), Constructor{}) {
|
||||
return [](const void *arg) -> void * {
|
||||
return new T(*reinterpret_cast<const T *>(arg));
|
||||
};
|
||||
static auto make_copy_constructor(const T *)
|
||||
-> decltype(new T(std::declval<const T>()), Constructor{}) {
|
||||
return [](const void *arg) -> void * { return new T(*reinterpret_cast<const T *>(arg)); };
|
||||
}
|
||||
|
||||
template <typename T, typename = enable_if_t<std::is_move_constructible<T>::value>>
|
||||
static auto make_move_constructor(const T *) -> decltype(new T(std::declval<T&&>()), Constructor{}) {
|
||||
static auto make_move_constructor(const T *)
|
||||
-> decltype(new T(std::declval<T &&>()), Constructor{}) {
|
||||
return [](const void *arg) -> void * {
|
||||
return new T(std::move(*const_cast<T *>(reinterpret_cast<const T *>(arg))));
|
||||
};
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
#include <cstdlib>
|
||||
|
||||
#if defined(__GNUG__)
|
||||
#include <cxxabi.h>
|
||||
# include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
@ -34,8 +34,8 @@ inline void erase_all(std::string &string, const std::string &search) {
|
||||
PYBIND11_NOINLINE void clean_type_id(std::string &name) {
|
||||
#if defined(__GNUG__)
|
||||
int status = 0;
|
||||
std::unique_ptr<char, void (*)(void *)> res {
|
||||
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free };
|
||||
std::unique_ptr<char, void (*)(void *)> res{
|
||||
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free};
|
||||
if (status == 0) {
|
||||
name = res.get();
|
||||
}
|
||||
@ -49,7 +49,8 @@ PYBIND11_NOINLINE void clean_type_id(std::string &name) {
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/// Return a string representation of a C++ type
|
||||
template <typename T> static std::string type_id() {
|
||||
template <typename T>
|
||||
static std::string type_id() {
|
||||
std::string name(typeid(T).name());
|
||||
detail::clean_type_id(name);
|
||||
return name;
|
||||
|
||||
@ -23,122 +23,145 @@
|
||||
// 2. we definitely want to ignore any MSVC warnings originating from Eigen code,
|
||||
// it is probably best to keep this around indefinitely.
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // C4127: conditional expression is constant
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4127) // C4127: conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit
|
||||
// move constructors that break things. We could detect this an explicitly copy, but an extra copy
|
||||
// of matrices seems highly undesirable.
|
||||
static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7");
|
||||
static_assert(EIGEN_VERSION_AT_LEAST(3, 2, 7),
|
||||
"Eigen support in pybind11 requires Eigen >= 3.2.7");
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
|
||||
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
|
||||
template <typename MatrixType> using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType> using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType>
|
||||
using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType>
|
||||
using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3,3,0)
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
|
||||
using EigenIndex = Eigen::Index;
|
||||
template<typename Scalar, int Flags, typename StorageIndex>
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
|
||||
#else
|
||||
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
|
||||
template<typename Scalar, int Flags, typename StorageIndex>
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
|
||||
#endif
|
||||
|
||||
// Matches Eigen::Map, Eigen::Ref, blocks, etc:
|
||||
template <typename T> using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>, std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
|
||||
template <typename T> using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>;
|
||||
template <typename T> using is_eigen_dense_plain = all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>;
|
||||
template <typename T> using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>;
|
||||
template <typename T>
|
||||
using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>,
|
||||
std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>;
|
||||
template <typename T>
|
||||
using is_eigen_dense_plain
|
||||
= all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>;
|
||||
// Test for objects inheriting from EigenBase<Derived> that aren't captured by the above. This
|
||||
// basically covers anything that can be assigned to a dense matrix but that don't have a typical
|
||||
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
|
||||
// SelfAdjointView fall into this category.
|
||||
template <typename T> using is_eigen_other = all_of<
|
||||
is_template_base_of<Eigen::EigenBase, T>,
|
||||
negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>
|
||||
>;
|
||||
template <typename T>
|
||||
using is_eigen_other
|
||||
= all_of<is_template_base_of<Eigen::EigenBase, T>,
|
||||
negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>>;
|
||||
|
||||
// Captures numpy/eigen conformability status (returned by EigenProps::conformable()):
|
||||
template <bool EigenRowMajor> struct EigenConformable {
|
||||
template <bool EigenRowMajor>
|
||||
struct EigenConformable {
|
||||
bool conformable = false;
|
||||
EigenIndex rows = 0, cols = 0;
|
||||
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
|
||||
bool negativestrides = false; // If true, do not use stride!
|
||||
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
|
||||
bool negativestrides = false; // If true, do not use stride!
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
EigenConformable(bool fits = false) : conformable{fits} {}
|
||||
// Matrix type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c,
|
||||
EigenIndex rstride, EigenIndex cstride) :
|
||||
conformable{true}, rows{r}, cols{c},
|
||||
//TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
|
||||
stride{EigenRowMajor ? (rstride > 0 ? rstride : 0) : (cstride > 0 ? cstride : 0) /* outer stride */,
|
||||
EigenRowMajor ? (cstride > 0 ? cstride : 0) : (rstride > 0 ? rstride : 0) /* inner stride */ },
|
||||
negativestrides{rstride < 0 || cstride < 0} {
|
||||
|
||||
}
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride)
|
||||
: conformable{true}, rows{r}, cols{c},
|
||||
// TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
|
||||
// http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
|
||||
stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
|
||||
: (cstride > 0 ? cstride : 0) /* outer stride */,
|
||||
EigenRowMajor ? (cstride > 0 ? cstride : 0)
|
||||
: (rstride > 0 ? rstride : 0) /* inner stride */},
|
||||
negativestrides{rstride < 0 || cstride < 0} {}
|
||||
// Vector type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride)
|
||||
: EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {}
|
||||
: EigenConformable(r, c, r == 1 ? c * stride : stride, c == 1 ? r : r * stride) {}
|
||||
|
||||
template <typename props> bool stride_compatible() const {
|
||||
template <typename props>
|
||||
bool stride_compatible() const {
|
||||
// To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
|
||||
// matching strides, or a dimension size of 1 (in which case the stride value is irrelevant)
|
||||
return
|
||||
!negativestrides &&
|
||||
(props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() ||
|
||||
(EigenRowMajor ? cols : rows) == 1) &&
|
||||
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() ||
|
||||
(EigenRowMajor ? rows : cols) == 1);
|
||||
// matching strides, or a dimension size of 1 (in which case the stride value is
|
||||
// irrelevant)
|
||||
return !negativestrides
|
||||
&& (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
|
||||
|| (EigenRowMajor ? cols : rows) == 1)
|
||||
&& (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
|
||||
|| (EigenRowMajor ? rows : cols) == 1);
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator bool() const { return conformable; }
|
||||
};
|
||||
|
||||
template <typename Type> struct eigen_extract_stride { using type = Type; };
|
||||
template <typename Type>
|
||||
struct eigen_extract_stride {
|
||||
using type = Type;
|
||||
};
|
||||
template <typename PlainObjectType, int MapOptions, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> { using type = StrideType; };
|
||||
struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
template <typename PlainObjectType, int Options, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> { using type = StrideType; };
|
||||
struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
|
||||
// Helper struct for extracting information from an Eigen type
|
||||
template <typename Type_> struct EigenProps {
|
||||
template <typename Type_>
|
||||
struct EigenProps {
|
||||
using Type = Type_;
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StrideType = typename eigen_extract_stride<Type>::type;
|
||||
static constexpr EigenIndex
|
||||
rows = Type::RowsAtCompileTime,
|
||||
cols = Type::ColsAtCompileTime,
|
||||
size = Type::SizeAtCompileTime;
|
||||
static constexpr bool
|
||||
row_major = Type::IsRowMajor,
|
||||
vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
|
||||
fixed_rows = rows != Eigen::Dynamic,
|
||||
fixed_cols = cols != Eigen::Dynamic,
|
||||
fixed = size != Eigen::Dynamic, // Fully-fixed size
|
||||
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
|
||||
static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
|
||||
size = Type::SizeAtCompileTime;
|
||||
static constexpr bool row_major = Type::IsRowMajor,
|
||||
vector
|
||||
= Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
|
||||
fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic,
|
||||
fixed = size != Eigen::Dynamic, // Fully-fixed size
|
||||
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
|
||||
|
||||
template <EigenIndex i, EigenIndex ifzero> using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
|
||||
static constexpr EigenIndex inner_stride = if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
|
||||
outer_stride = if_zero<StrideType::OuterStrideAtCompileTime,
|
||||
vector ? size : row_major ? cols : rows>::value;
|
||||
static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic;
|
||||
static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1;
|
||||
static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1;
|
||||
template <EigenIndex i, EigenIndex ifzero>
|
||||
using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
|
||||
static constexpr EigenIndex inner_stride
|
||||
= if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
|
||||
outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
|
||||
vector ? size
|
||||
: row_major ? cols
|
||||
: rows > ::value;
|
||||
static constexpr bool dynamic_stride
|
||||
= inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic;
|
||||
static constexpr bool requires_row_major
|
||||
= !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1;
|
||||
static constexpr bool requires_col_major
|
||||
= !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1;
|
||||
|
||||
// Takes an input array and determines whether we can make it fit into the Eigen type. If
|
||||
// the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector
|
||||
@ -151,11 +174,9 @@ template <typename Type_> struct EigenProps {
|
||||
|
||||
if (dims == 2) { // Matrix type: require exact match (or dynamic)
|
||||
|
||||
EigenIndex
|
||||
np_rows = a.shape(0),
|
||||
np_cols = a.shape(1),
|
||||
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
|
||||
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
EigenIndex np_rows = a.shape(0), np_cols = a.shape(1),
|
||||
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
|
||||
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows)
|
||||
|| (PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols)) {
|
||||
return false;
|
||||
@ -164,10 +185,10 @@ template <typename Type_> struct EigenProps {
|
||||
return {np_rows, np_cols, np_rstride, np_cstride};
|
||||
}
|
||||
|
||||
// Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever
|
||||
// is used, we want the (single) numpy stride value.
|
||||
// Otherwise we're storing an n-vector. Only one of the strides will be used, but
|
||||
// whichever is used, we want the (single) numpy stride value.
|
||||
const EigenIndex n = a.shape(0),
|
||||
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
|
||||
if (vector) { // Eigen type is a compile-time vector
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n) {
|
||||
@ -190,38 +211,41 @@ template <typename Type_> struct EigenProps {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) {
|
||||
return false;
|
||||
}
|
||||
return {n, 1, stride};
|
||||
return {n, 1, stride};
|
||||
}
|
||||
|
||||
static constexpr bool show_writeable = is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
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 bool show_f_contiguous
|
||||
= !show_c_contiguous && show_order && requires_col_major;
|
||||
|
||||
static constexpr auto descriptor =
|
||||
const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name +
|
||||
const_name("[") + const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) +
|
||||
const_name(", ") + const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) +
|
||||
const_name("]") +
|
||||
// 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.
|
||||
const_name<show_writeable>(", flags.writeable", "") +
|
||||
const_name<show_c_contiguous>(", flags.c_contiguous", "") +
|
||||
const_name<show_f_contiguous>(", flags.f_contiguous", "") +
|
||||
const_name("]");
|
||||
static constexpr auto descriptor
|
||||
= const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
|
||||
+ const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
|
||||
+ const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]")
|
||||
+
|
||||
// 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.
|
||||
const_name<show_writeable>(", flags.writeable", "")
|
||||
+ const_name<show_c_contiguous>(", flags.c_contiguous", "")
|
||||
+ const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]");
|
||||
};
|
||||
|
||||
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
|
||||
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
|
||||
template <typename props> handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
|
||||
template <typename props>
|
||||
handle
|
||||
eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
|
||||
constexpr ssize_t elem_size = sizeof(typename props::Scalar);
|
||||
array a;
|
||||
if (props::vector) {
|
||||
a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base);
|
||||
a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base);
|
||||
} else {
|
||||
a = array({src.rows(), src.cols()},
|
||||
{elem_size * src.rowStride(), elem_size * src.colStride()},
|
||||
@ -247,10 +271,10 @@ handle eigen_ref_array(Type &src, handle parent = none()) {
|
||||
return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value);
|
||||
}
|
||||
|
||||
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy
|
||||
// array that references the encapsulated data with a python-side reference to the capsule to tie
|
||||
// its destruction to that of any dependent python objects. Const-ness is determined by whether or
|
||||
// not the Type of the pointer given is const.
|
||||
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
|
||||
// numpy array that references the encapsulated data with a python-side reference to the capsule to
|
||||
// tie its destruction to that of any dependent python objects. Const-ness is determined by
|
||||
// whether or not the Type of the pointer given is const.
|
||||
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
|
||||
handle eigen_encapsulate(Type *src) {
|
||||
capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
|
||||
@ -259,7 +283,7 @@ handle eigen_encapsulate(Type *src) {
|
||||
|
||||
// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense
|
||||
// types.
|
||||
template<typename Type>
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using props = EigenProps<Type>;
|
||||
@ -307,7 +331,6 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Cast implementation
|
||||
template <typename CType>
|
||||
static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
|
||||
@ -330,7 +353,6 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// Normal returned non-reference, non-const value:
|
||||
static handle cast(Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
@ -367,30 +389,31 @@ public:
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type*() { return &value; }
|
||||
operator Type *() { return &value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&() { return value; }
|
||||
operator Type &() { return value; }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&&() && { return std::move(value); }
|
||||
template <typename T> using cast_op_type = movable_cast_op_type<T>;
|
||||
operator Type &&() && { return std::move(value); }
|
||||
template <typename T>
|
||||
using cast_op_type = movable_cast_op_type<T>;
|
||||
|
||||
private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
// Base class for casting reference/map/block/etc. objects back to python.
|
||||
template <typename MapType> struct eigen_map_caster {
|
||||
template <typename MapType>
|
||||
struct eigen_map_caster {
|
||||
private:
|
||||
using props = EigenProps<MapType>;
|
||||
|
||||
public:
|
||||
|
||||
// Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has
|
||||
// to stay around), but we'll allow it under the assumption that you know what you're doing (and
|
||||
// have an appropriate keep_alive in place). We return a numpy array pointing directly at the
|
||||
// ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note
|
||||
// that this means you need to ensure you don't destroy the object in some other way (e.g. with
|
||||
// an appropriate keep_alive, or with a reference to a statically allocated matrix).
|
||||
// to stay around), but we'll allow it under the assumption that you know what you're doing
|
||||
// (and have an appropriate keep_alive in place). We return a numpy array pointing directly at
|
||||
// the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
|
||||
// Note that this means you need to ensure you don't destroy the object in some other way (e.g.
|
||||
// with an appropriate keep_alive, or with a reference to a statically allocated matrix).
|
||||
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
|
||||
switch (policy) {
|
||||
case return_value_policy::copy:
|
||||
@ -414,43 +437,50 @@ public:
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator MapType() = delete;
|
||||
template <typename> using cast_op_type = MapType;
|
||||
template <typename>
|
||||
using cast_op_type = MapType;
|
||||
};
|
||||
|
||||
// We can return any map-like object (but can only load Refs, specialized next):
|
||||
template <typename Type> struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>>
|
||||
: eigen_map_caster<Type> {};
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> : eigen_map_caster<Type> {};
|
||||
|
||||
// Loader for Ref<...> arguments. See the documentation for info on how to make this work without
|
||||
// copying (it requires some extra effort in many cases).
|
||||
template <typename PlainObjectType, typename StrideType>
|
||||
struct type_caster<
|
||||
Eigen::Ref<PlainObjectType, 0, StrideType>,
|
||||
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>
|
||||
> : public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
|
||||
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
|
||||
: public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
|
||||
private:
|
||||
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
|
||||
using props = EigenProps<Type>;
|
||||
using Scalar = typename props::Scalar;
|
||||
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
|
||||
using Array = array_t<Scalar, array::forcecast |
|
||||
((props::row_major ? props::inner_stride : props::outer_stride) == 1 ? array::c_style :
|
||||
(props::row_major ? props::outer_stride : props::inner_stride) == 1 ? array::f_style : 0)>;
|
||||
using Array
|
||||
= array_t<Scalar,
|
||||
array::forcecast
|
||||
| ((props::row_major ? props::inner_stride : props::outer_stride) == 1
|
||||
? array::c_style
|
||||
: (props::row_major ? props::outer_stride : props::inner_stride) == 1
|
||||
? array::f_style
|
||||
: 0)>;
|
||||
static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value;
|
||||
// Delay construction (these have no default constructor)
|
||||
std::unique_ptr<MapType> map;
|
||||
std::unique_ptr<Type> ref;
|
||||
// Our array. When possible, this is just a numpy array pointing to the source data, but
|
||||
// sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible
|
||||
// layout, or is an array of a type that needs to be converted). Using a numpy temporary
|
||||
// (rather than an Eigen temporary) saves an extra copy when we need both type conversion and
|
||||
// storage order conversion. (Note that we refuse to use this temporary copy when loading an
|
||||
// argument for a Ref<M> with M non-const, i.e. a read-write reference).
|
||||
// sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
|
||||
// incompatible layout, or is an array of a type that needs to be converted). Using a numpy
|
||||
// temporary (rather than an Eigen temporary) saves an extra copy when we need both type
|
||||
// conversion and storage order conversion. (Note that we refuse to use this temporary copy
|
||||
// when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
|
||||
Array copy_or_ref;
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
// First check whether what we have is already an array of the right type. If not, we can't
|
||||
// avoid a copy (because the copy is also going to do type conversion).
|
||||
// First check whether what we have is already an array of the right type. If not, we
|
||||
// can't avoid a copy (because the copy is also going to do type conversion).
|
||||
bool need_copy = !isinstance<Array>(src);
|
||||
|
||||
EigenConformable<props::row_major> fits;
|
||||
@ -469,8 +499,7 @@ public:
|
||||
} else {
|
||||
copy_or_ref = std::move(aref);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
need_copy = true;
|
||||
}
|
||||
}
|
||||
@ -496,54 +525,76 @@ public:
|
||||
}
|
||||
|
||||
ref.reset();
|
||||
map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner())));
|
||||
map.reset(new MapType(data(copy_or_ref),
|
||||
fits.rows,
|
||||
fits.cols,
|
||||
make_stride(fits.stride.outer(), fits.stride.inner())));
|
||||
ref.reset(new Type(*map));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type*() { return ref.get(); }
|
||||
operator Type *() { return ref.get(); }
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator Type&() { return *ref; }
|
||||
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
|
||||
operator Type &() { return *ref; }
|
||||
template <typename _T>
|
||||
using cast_op_type = pybind11::detail::cast_op_type<_T>;
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0>
|
||||
Scalar *data(Array &a) { return a.mutable_data(); }
|
||||
Scalar *data(Array &a) {
|
||||
return a.mutable_data();
|
||||
}
|
||||
|
||||
template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0>
|
||||
const Scalar *data(Array &a) { return a.data(); }
|
||||
const Scalar *data(Array &a) {
|
||||
return a.data();
|
||||
}
|
||||
|
||||
// Attempt to figure out a constructor of `Stride` that will work.
|
||||
// If both strides are fixed, use a default constructor:
|
||||
template <typename S> using stride_ctor_default = bool_constant<
|
||||
S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic &&
|
||||
std::is_default_constructible<S>::value>;
|
||||
template <typename S>
|
||||
using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_default_constructible<S>::value>;
|
||||
// Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
|
||||
// Eigen::Stride, and use it:
|
||||
template <typename S> using stride_ctor_dual = bool_constant<
|
||||
!stride_ctor_default<S>::value && std::is_constructible<S, EigenIndex, EigenIndex>::value>;
|
||||
template <typename S>
|
||||
using stride_ctor_dual
|
||||
= bool_constant<!stride_ctor_default<S>::value
|
||||
&& std::is_constructible<S, EigenIndex, EigenIndex>::value>;
|
||||
// Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use
|
||||
// it (passing whichever stride is dynamic).
|
||||
template <typename S> using stride_ctor_outer = bool_constant<
|
||||
!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value &&
|
||||
S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic &&
|
||||
std::is_constructible<S, EigenIndex>::value>;
|
||||
template <typename S> using stride_ctor_inner = bool_constant<
|
||||
!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value &&
|
||||
S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic &&
|
||||
std::is_constructible<S, EigenIndex>::value>;
|
||||
template <typename S>
|
||||
using stride_ctor_outer
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::OuterStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
template <typename S>
|
||||
using stride_ctor_inner
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::InnerStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex) { return S(); }
|
||||
static S make_stride(EigenIndex, EigenIndex) {
|
||||
return S();
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); }
|
||||
static S make_stride(EigenIndex outer, EigenIndex inner) {
|
||||
return S(outer, inner);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); }
|
||||
static S make_stride(EigenIndex outer, EigenIndex) {
|
||||
return S(outer);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); }
|
||||
|
||||
static S make_stride(EigenIndex, EigenIndex inner) {
|
||||
return S(inner);
|
||||
}
|
||||
};
|
||||
|
||||
// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not
|
||||
@ -553,14 +604,18 @@ private:
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
|
||||
protected:
|
||||
using Matrix = Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
|
||||
using Matrix
|
||||
= Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
|
||||
using props = EigenProps<Matrix>;
|
||||
|
||||
public:
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
handle h = eigen_encapsulate<props>(new Matrix(src));
|
||||
return h;
|
||||
}
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); }
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
return cast(*src, policy, parent);
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
@ -569,10 +624,11 @@ public:
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator Type() = delete;
|
||||
template <typename> using cast_op_type = Type;
|
||||
template <typename>
|
||||
using cast_op_type = Type;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
|
||||
@ -586,8 +642,7 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
|
||||
auto obj = reinterpret_borrow<object>(src);
|
||||
object sparse_module = module_::import("scipy.sparse");
|
||||
object matrix_type = sparse_module.attr(
|
||||
rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
if (!type::handle_of(obj).is(matrix_type)) {
|
||||
try {
|
||||
@ -608,32 +663,36 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
}
|
||||
|
||||
value = EigenMapSparseMatrix<Scalar,
|
||||
Type::Flags & (Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(
|
||||
shape[0].cast<Index>(), shape[1].cast<Index>(), nnz,
|
||||
outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data());
|
||||
Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(shape[0].cast<Index>(),
|
||||
shape[1].cast<Index>(),
|
||||
nnz,
|
||||
outerIndices.mutable_data(),
|
||||
innerIndices.mutable_data(),
|
||||
values.mutable_data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
const_cast<Type&>(src).makeCompressed();
|
||||
const_cast<Type &>(src).makeCompressed();
|
||||
|
||||
object matrix_type = module_::import("scipy.sparse").attr(
|
||||
rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
object matrix_type
|
||||
= module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
array data(src.nonZeros(), src.valuePtr());
|
||||
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
|
||||
array innerIndices(src.nonZeros(), src.innerIndexPtr());
|
||||
|
||||
return matrix_type(
|
||||
std::make_tuple(data, innerIndices, outerIndices),
|
||||
std::make_pair(src.rows(), src.cols())
|
||||
).release();
|
||||
return matrix_type(std::make_tuple(data, innerIndices, outerIndices),
|
||||
std::make_pair(src.rows(), src.cols()))
|
||||
.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type, const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[")
|
||||
+ npy_format_descriptor<Scalar>::name + const_name("]"));
|
||||
PYBIND11_TYPE_CASTER(Type,
|
||||
const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
|
||||
"scipy.sparse.csc_matrix[")
|
||||
+ npy_format_descriptor<Scalar>::name + const_name("]"));
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
@ -16,21 +16,17 @@
|
||||
#include <vector>
|
||||
|
||||
#if defined(PYPY_VERSION)
|
||||
# error Embedding the interpreter is not supported with PyPy
|
||||
# error Embedding the interpreter is not supported with PyPy
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" PyObject *pybind11_init_impl_##name(); \
|
||||
extern "C" PyObject *pybind11_init_impl_##name() { \
|
||||
return pybind11_init_wrapper_##name(); \
|
||||
}
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" PyObject *pybind11_init_impl_##name(); \
|
||||
extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); }
|
||||
#else
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" void pybind11_init_impl_##name(); \
|
||||
extern "C" void pybind11_init_impl_##name() { \
|
||||
pybind11_init_wrapper_##name(); \
|
||||
}
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" void pybind11_init_impl_##name(); \
|
||||
extern "C" void pybind11_init_impl_##name() { pybind11_init_wrapper_##name(); }
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
@ -72,7 +68,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
|
||||
struct embedded_module {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
using init_t = PyObject *(*)();
|
||||
using init_t = PyObject *(*) ();
|
||||
#else
|
||||
using init_t = void (*)();
|
||||
#endif
|
||||
@ -106,10 +102,10 @@ inline wchar_t *widen_chars(const char *safe_arg) {
|
||||
wchar_t *widened_arg = nullptr;
|
||||
|
||||
// warning C4996: 'mbstowcs': This function or variable may be unsafe.
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
# if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4996)
|
||||
# endif
|
||||
|
||||
# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS
|
||||
size_t count = std::strlen(safe_arg);
|
||||
@ -121,9 +117,9 @@ inline wchar_t *widen_chars(const char *safe_arg) {
|
||||
std::mbstowcs(widened_arg, safe_arg, count + 1);
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
# if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
# endif
|
||||
|
||||
#endif
|
||||
return widened_arg;
|
||||
|
||||
@ -11,24 +11,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
inline void ensure_builtins_in_globals(object &global) {
|
||||
#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000
|
||||
// Running exec and eval on Python 2 and 3 adds `builtins` module under
|
||||
// `__builtins__` key to globals if not yet present.
|
||||
// Python 3.8 made PyRun_String behave similarly. Let's also do that for
|
||||
// older versions, for consistency. This was missing from PyPy3.8 7.3.7.
|
||||
if (!global.contains("__builtins__"))
|
||||
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
|
||||
#else
|
||||
(void) global;
|
||||
#endif
|
||||
#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000
|
||||
// Running exec and eval on Python 2 and 3 adds `builtins` module under
|
||||
// `__builtins__` key to globals if not yet present.
|
||||
// Python 3.8 made PyRun_String behave similarly. Let's also do that for
|
||||
// older versions, for consistency. This was missing from PyPy3.8 7.3.7.
|
||||
if (!global.contains("__builtins__"))
|
||||
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
|
||||
#else
|
||||
(void) global;
|
||||
#endif
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
@ -58,10 +58,17 @@ object eval(const str &expr, object global = globals(), object local = object())
|
||||
|
||||
int start = 0;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_input; break;
|
||||
case eval_single_statement: start = Py_single_input; break;
|
||||
case eval_statements: start = Py_file_input; break;
|
||||
default: pybind11_fail("invalid evaluation mode");
|
||||
case eval_expr:
|
||||
start = Py_eval_input;
|
||||
break;
|
||||
case eval_single_statement:
|
||||
start = Py_single_input;
|
||||
break;
|
||||
case eval_statements:
|
||||
start = Py_file_input;
|
||||
break;
|
||||
default:
|
||||
pybind11_fail("invalid evaluation mode");
|
||||
}
|
||||
|
||||
PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
|
||||
@ -74,8 +81,7 @@ object eval(const str &expr, object global = globals(), object local = object())
|
||||
template <eval_mode mode = eval_expr, size_t N>
|
||||
object eval(const char (&s)[N], object global = globals(), object local = object()) {
|
||||
/* Support raw string literals by removing common leading whitespace */
|
||||
auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s))
|
||||
: str(s);
|
||||
auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) : str(s);
|
||||
return eval<mode>(expr, global, local);
|
||||
}
|
||||
|
||||
@ -112,28 +118,34 @@ object eval_file(str fname, object global = globals(), object local = object())
|
||||
|
||||
int start = 0;
|
||||
switch (mode) {
|
||||
case eval_expr: start = Py_eval_input; break;
|
||||
case eval_single_statement: start = Py_single_input; break;
|
||||
case eval_statements: start = Py_file_input; break;
|
||||
default: pybind11_fail("invalid evaluation mode");
|
||||
case eval_expr:
|
||||
start = Py_eval_input;
|
||||
break;
|
||||
case eval_single_statement:
|
||||
start = Py_single_input;
|
||||
break;
|
||||
case eval_statements:
|
||||
start = Py_file_input;
|
||||
break;
|
||||
default:
|
||||
pybind11_fail("invalid evaluation mode");
|
||||
}
|
||||
|
||||
int closeFile = 1;
|
||||
std::string fname_str = (std::string) fname;
|
||||
#if PY_VERSION_HEX >= 0x03040000
|
||||
# if PY_VERSION_HEX >= 0x03040000
|
||||
FILE *f = _Py_fopen_obj(fname.ptr(), "r");
|
||||
#elif PY_VERSION_HEX >= 0x03000000
|
||||
# elif PY_VERSION_HEX >= 0x03000000
|
||||
FILE *f = _Py_fopen(fname.ptr(), "r");
|
||||
#else
|
||||
# else
|
||||
/* No unicode support in open() :( */
|
||||
auto fobj = reinterpret_steal<object>(PyFile_FromString(
|
||||
const_cast<char *>(fname_str.c_str()),
|
||||
const_cast<char*>("r")));
|
||||
auto fobj = reinterpret_steal<object>(
|
||||
PyFile_FromString(const_cast<char *>(fname_str.c_str()), const_cast<char *>("r")));
|
||||
FILE *f = nullptr;
|
||||
if (fobj)
|
||||
f = PyFile_AsFile(fobj.ptr());
|
||||
closeFile = 0;
|
||||
#endif
|
||||
# endif
|
||||
if (!f) {
|
||||
PyErr_Clear();
|
||||
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
|
||||
@ -142,20 +154,19 @@ object eval_file(str fname, object global = globals(), object local = object())
|
||||
// In Python2, this should be encoded by getfilesystemencoding.
|
||||
// We don't boher setting it since Python2 is past EOL anyway.
|
||||
// See PR#3233
|
||||
#if PY_VERSION_HEX >= 0x03000000
|
||||
# if PY_VERSION_HEX >= 0x03000000
|
||||
if (!global.contains("__file__")) {
|
||||
global["__file__"] = std::move(fname);
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
|
||||
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
|
||||
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
|
||||
local.ptr());
|
||||
# if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
|
||||
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), local.ptr());
|
||||
(void) closeFile;
|
||||
#else
|
||||
PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
|
||||
local.ptr(), closeFile);
|
||||
#endif
|
||||
# else
|
||||
PyObject *result
|
||||
= PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile);
|
||||
# endif
|
||||
|
||||
if (!result) {
|
||||
throw error_already_set();
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
@ -19,7 +20,7 @@ template <typename Return, typename... Args>
|
||||
struct type_caster<std::function<Return(Args...)>> {
|
||||
using type = std::function<Return(Args...)>;
|
||||
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
||||
using function_type = Return (*) (Args...);
|
||||
using function_type = Return (*)(Args...);
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
@ -76,7 +77,9 @@ public:
|
||||
// This triggers a syntax error under very special conditions (very weird indeed).
|
||||
explicit
|
||||
#endif
|
||||
func_handle(function &&f_) noexcept : f(std::move(f_)) {}
|
||||
func_handle(function &&f_) noexcept
|
||||
: f(std::move(f_)) {
|
||||
}
|
||||
func_handle(const func_handle &f_) { operator=(f_); }
|
||||
func_handle &operator=(const func_handle &f_) {
|
||||
gil_scoped_acquire acq;
|
||||
@ -118,8 +121,10 @@ public:
|
||||
return cpp_function(std::forward<Func>(f_), policy).release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(type, const_name("Callable[[") + concat(make_caster<Args>::name...) + const_name("], ")
|
||||
+ make_caster<retval_type>::name + const_name("]"));
|
||||
PYBIND11_TYPE_CASTER(type,
|
||||
const_name("Callable[[") + concat(make_caster<Args>::name...)
|
||||
+ const_name("], ") + make_caster<retval_type>::name
|
||||
+ const_name("]"));
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// forward declarations
|
||||
@ -22,7 +21,6 @@ PyThreadState *get_thread_state_unchecked();
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
|
||||
|
||||
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
||||
@ -64,11 +62,11 @@ public:
|
||||
|
||||
if (!tstate) {
|
||||
tstate = PyThreadState_New(internals.istate);
|
||||
#if !defined(NDEBUG)
|
||||
# if !defined(NDEBUG)
|
||||
if (!tstate) {
|
||||
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
tstate->gilstate_counter = 0;
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
|
||||
} else {
|
||||
@ -82,26 +80,24 @@ public:
|
||||
inc_ref();
|
||||
}
|
||||
|
||||
void inc_ref() {
|
||||
++tstate->gilstate_counter;
|
||||
}
|
||||
void inc_ref() { ++tstate->gilstate_counter; }
|
||||
|
||||
PYBIND11_NOINLINE void dec_ref() {
|
||||
--tstate->gilstate_counter;
|
||||
#if !defined(NDEBUG)
|
||||
# if !defined(NDEBUG)
|
||||
if (detail::get_thread_state_unchecked() != tstate) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
|
||||
}
|
||||
if (tstate->gilstate_counter < 0) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
if (tstate->gilstate_counter == 0) {
|
||||
#if !defined(NDEBUG)
|
||||
# if !defined(NDEBUG)
|
||||
if (!release) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
PyThreadState_Clear(tstate);
|
||||
if (active) {
|
||||
PyThreadState_DeleteCurrent();
|
||||
@ -116,9 +112,7 @@ public:
|
||||
/// could be shutting down when this is called, as thread deletion is not
|
||||
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||
/// protect subsequent code.
|
||||
PYBIND11_NOINLINE void disarm() {
|
||||
active = false;
|
||||
}
|
||||
PYBIND11_NOINLINE void disarm() { active = false; }
|
||||
|
||||
PYBIND11_NOINLINE ~gil_scoped_acquire() {
|
||||
dec_ref();
|
||||
@ -126,6 +120,7 @@ public:
|
||||
PyEval_SaveThread();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PyThreadState *tstate = nullptr;
|
||||
bool release = true;
|
||||
@ -154,9 +149,7 @@ public:
|
||||
/// could be shutting down when this is called, as thread deletion is not
|
||||
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||
/// protect subsequent code.
|
||||
PYBIND11_NOINLINE void disarm() {
|
||||
active = false;
|
||||
}
|
||||
PYBIND11_NOINLINE void disarm() { active = false; }
|
||||
|
||||
~gil_scoped_release() {
|
||||
if (!tstate) {
|
||||
@ -173,6 +166,7 @@ public:
|
||||
PYBIND11_TLS_REPLACE_VALUE(key, tstate);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PyThreadState *tstate;
|
||||
bool disassoc;
|
||||
@ -181,6 +175,7 @@ private:
|
||||
#elif defined(PYPY_VERSION)
|
||||
class gil_scoped_acquire {
|
||||
PyGILState_STATE state;
|
||||
|
||||
public:
|
||||
gil_scoped_acquire() { state = PyGILState_Ensure(); }
|
||||
~gil_scoped_acquire() { PyGILState_Release(state); }
|
||||
@ -189,6 +184,7 @@ public:
|
||||
|
||||
class gil_scoped_release {
|
||||
PyThreadState *state;
|
||||
|
||||
public:
|
||||
gil_scoped_release() { state = PyEval_SaveThread(); }
|
||||
~gil_scoped_release() { PyEval_RestoreThread(state); }
|
||||
|
||||
@ -58,31 +58,23 @@ private:
|
||||
size_t utf8_remainder() const {
|
||||
const auto rbase = std::reverse_iterator<char *>(pbase());
|
||||
const auto rpptr = std::reverse_iterator<char *>(pptr());
|
||||
auto is_ascii = [](char c) {
|
||||
return (static_cast<unsigned char>(c) & 0x80) == 0x00;
|
||||
};
|
||||
auto is_leading = [](char c) {
|
||||
return (static_cast<unsigned char>(c) & 0xC0) == 0xC0;
|
||||
};
|
||||
auto is_leading_2b = [](char c) {
|
||||
return static_cast<unsigned char>(c) <= 0xDF;
|
||||
};
|
||||
auto is_leading_3b = [](char c) {
|
||||
return static_cast<unsigned char>(c) <= 0xEF;
|
||||
};
|
||||
auto is_ascii = [](char c) { return (static_cast<unsigned char>(c) & 0x80) == 0x00; };
|
||||
auto is_leading = [](char c) { return (static_cast<unsigned char>(c) & 0xC0) == 0xC0; };
|
||||
auto is_leading_2b = [](char c) { return static_cast<unsigned char>(c) <= 0xDF; };
|
||||
auto is_leading_3b = [](char c) { return static_cast<unsigned char>(c) <= 0xEF; };
|
||||
// If the last character is ASCII, there are no incomplete code points
|
||||
if (is_ascii(*rpptr)) {
|
||||
return 0;
|
||||
}
|
||||
// Otherwise, work back from the end of the buffer and find the first
|
||||
// UTF-8 leading byte
|
||||
const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase;
|
||||
const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase;
|
||||
const auto leading = std::find_if(rpptr, rpend, is_leading);
|
||||
if (leading == rbase) {
|
||||
return 0;
|
||||
}
|
||||
const auto dist = static_cast<size_t>(leading - rpptr);
|
||||
size_t remainder = 0;
|
||||
const auto dist = static_cast<size_t>(leading - rpptr);
|
||||
size_t remainder = 0;
|
||||
|
||||
if (dist == 0) {
|
||||
remainder = 1; // 1-byte code point is impossible
|
||||
@ -103,7 +95,7 @@ private:
|
||||
if (pbase() != pptr()) { // If buffer is not empty
|
||||
gil_scoped_acquire tmp;
|
||||
// This subtraction cannot be negative, so dropping the sign.
|
||||
auto size = static_cast<size_t>(pptr() - pbase());
|
||||
auto size = static_cast<size_t>(pptr() - pbase());
|
||||
size_t remainder = utf8_remainder();
|
||||
|
||||
if (size > remainder) {
|
||||
@ -122,9 +114,7 @@ private:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sync() override {
|
||||
return _sync();
|
||||
}
|
||||
int sync() override { return _sync(); }
|
||||
|
||||
public:
|
||||
explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024)
|
||||
@ -133,17 +123,14 @@ public:
|
||||
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
|
||||
}
|
||||
|
||||
pythonbuf(pythonbuf&&) = default;
|
||||
pythonbuf(pythonbuf &&) = default;
|
||||
|
||||
/// Sync before destroy
|
||||
~pythonbuf() override {
|
||||
_sync();
|
||||
}
|
||||
~pythonbuf() override { _sync(); }
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
|
||||
/** \rst
|
||||
This a move-only guard that redirects output.
|
||||
|
||||
@ -183,9 +170,7 @@ public:
|
||||
old = costream.rdbuf(&buffer);
|
||||
}
|
||||
|
||||
~scoped_ostream_redirect() {
|
||||
costream.rdbuf(old);
|
||||
}
|
||||
~scoped_ostream_redirect() { costream.rdbuf(old); }
|
||||
|
||||
scoped_ostream_redirect(const scoped_ostream_redirect &) = delete;
|
||||
scoped_ostream_redirect(scoped_ostream_redirect &&other) = default;
|
||||
@ -193,7 +178,6 @@ public:
|
||||
scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete;
|
||||
};
|
||||
|
||||
|
||||
/** \rst
|
||||
Like `scoped_ostream_redirect`, but redirects cerr by default. This class
|
||||
is provided primary to make ``py::call_guard`` easier to make.
|
||||
@ -213,7 +197,6 @@ public:
|
||||
: scoped_ostream_redirect(costream, pyostream) {}
|
||||
};
|
||||
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// Class to redirect output as a context manager. C++ backend.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -16,12 +16,50 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Enumeration with all supported operator types
|
||||
enum op_id : int {
|
||||
op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift,
|
||||
op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert,
|
||||
op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le,
|
||||
op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift,
|
||||
op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero,
|
||||
op_repr, op_truediv, op_itruediv, op_hash
|
||||
op_add,
|
||||
op_sub,
|
||||
op_mul,
|
||||
op_div,
|
||||
op_mod,
|
||||
op_divmod,
|
||||
op_pow,
|
||||
op_lshift,
|
||||
op_rshift,
|
||||
op_and,
|
||||
op_xor,
|
||||
op_or,
|
||||
op_neg,
|
||||
op_pos,
|
||||
op_abs,
|
||||
op_invert,
|
||||
op_int,
|
||||
op_long,
|
||||
op_float,
|
||||
op_str,
|
||||
op_cmp,
|
||||
op_gt,
|
||||
op_ge,
|
||||
op_lt,
|
||||
op_le,
|
||||
op_eq,
|
||||
op_ne,
|
||||
op_iadd,
|
||||
op_isub,
|
||||
op_imul,
|
||||
op_idiv,
|
||||
op_imod,
|
||||
op_ilshift,
|
||||
op_irshift,
|
||||
op_iand,
|
||||
op_ixor,
|
||||
op_ior,
|
||||
op_complex,
|
||||
op_bool,
|
||||
op_nonzero,
|
||||
op_repr,
|
||||
op_truediv,
|
||||
op_itruediv,
|
||||
op_hash
|
||||
};
|
||||
|
||||
enum op_type : int {
|
||||
@ -30,126 +68,145 @@ enum op_type : int {
|
||||
op_u /* unary operator */
|
||||
};
|
||||
|
||||
struct self_t { };
|
||||
struct self_t {};
|
||||
static const self_t self = self_t();
|
||||
|
||||
/// Type for an unused type slot
|
||||
struct undefined_t { };
|
||||
struct undefined_t {};
|
||||
|
||||
/// Don't warn about an unused variable
|
||||
inline self_t __self() { return self; }
|
||||
|
||||
/// base template of operator implementations
|
||||
template <op_id, op_type, typename B, typename L, typename R> struct op_impl { };
|
||||
template <op_id, op_type, typename B, typename L, typename R>
|
||||
struct op_impl {};
|
||||
|
||||
/// Operator implementation generator
|
||||
template <op_id id, op_type ot, typename L, typename R> struct op_ {
|
||||
template <typename Class, typename... Extra> void execute(Class &cl, const Extra&... extra) const {
|
||||
template <op_id id, op_type ot, typename L, typename R>
|
||||
struct op_ {
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra &...extra) const {
|
||||
using Base = typename Class::type;
|
||||
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv) ||
|
||||
PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
|
||||
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
|
||||
&op::execute, is_operator(), extra...);
|
||||
#endif
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv)
|
||||
|| PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
|
||||
cl.def(id == op_itruediv ? "__idiv__"
|
||||
: ot == op_l ? "__div__"
|
||||
: "__rdiv__",
|
||||
&op::execute,
|
||||
is_operator(),
|
||||
extra...);
|
||||
#endif
|
||||
}
|
||||
template <typename Class, typename... Extra> void execute_cast(Class &cl, const Extra&... extra) const {
|
||||
template <typename Class, typename... Extra>
|
||||
void execute_cast(Class &cl, const Extra &...extra) const {
|
||||
using Base = typename Class::type;
|
||||
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute_cast, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (id == op_truediv || id == op_itruediv)
|
||||
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
|
||||
&op::execute, is_operator(), extra...);
|
||||
#endif
|
||||
cl.def(id == op_itruediv ? "__idiv__"
|
||||
: ot == op_l ? "__div__"
|
||||
: "__rdiv__",
|
||||
&op::execute,
|
||||
is_operator(),
|
||||
extra...);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \
|
||||
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const* name() { return "__" #id "__"; } \
|
||||
static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \
|
||||
static B execute_cast(const L &l, const R &r) { return B(expr); } \
|
||||
}; \
|
||||
template <typename B, typename L, typename R> struct op_impl<op_##id, op_r, B, L, R> { \
|
||||
static char const* name() { return "__" #rid "__"; } \
|
||||
static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \
|
||||
static B execute_cast(const R &r, const L &l) { return B(expr); } \
|
||||
}; \
|
||||
inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) { \
|
||||
return op_<op_##id, op_l, self_t, self_t>(); \
|
||||
} \
|
||||
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
|
||||
return op_<op_##id, op_l, self_t, T>(); \
|
||||
} \
|
||||
template <typename T> op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) { \
|
||||
return op_<op_##id, op_r, T, self_t>(); \
|
||||
}
|
||||
#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \
|
||||
template <typename B, typename L, typename R> \
|
||||
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const *name() { return "__" #id "__"; } \
|
||||
static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \
|
||||
static B execute_cast(const L &l, const R &r) { return B(expr); } \
|
||||
}; \
|
||||
template <typename B, typename L, typename R> \
|
||||
struct op_impl<op_##id, op_r, B, L, R> { \
|
||||
static char const *name() { return "__" #rid "__"; } \
|
||||
static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \
|
||||
static B execute_cast(const R &r, const L &l) { return B(expr); } \
|
||||
}; \
|
||||
inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) { \
|
||||
return op_<op_##id, op_l, self_t, self_t>(); \
|
||||
} \
|
||||
template <typename T> \
|
||||
op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
|
||||
return op_<op_##id, op_l, self_t, T>(); \
|
||||
} \
|
||||
template <typename T> \
|
||||
op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) { \
|
||||
return op_<op_##id, op_r, T, self_t>(); \
|
||||
}
|
||||
|
||||
#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \
|
||||
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const* name() { return "__" #id "__"; } \
|
||||
static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \
|
||||
static B execute_cast(L &l, const R &r) { return B(expr); } \
|
||||
}; \
|
||||
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
|
||||
return op_<op_##id, op_l, self_t, T>(); \
|
||||
}
|
||||
#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \
|
||||
template <typename B, typename L, typename R> \
|
||||
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const *name() { return "__" #id "__"; } \
|
||||
static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \
|
||||
static B execute_cast(L &l, const R &r) { return B(expr); } \
|
||||
}; \
|
||||
template <typename T> \
|
||||
op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
|
||||
return op_<op_##id, op_l, self_t, T>(); \
|
||||
}
|
||||
|
||||
#define PYBIND11_UNARY_OPERATOR(id, op, expr) \
|
||||
template <typename B, typename L> struct op_impl<op_##id, op_u, B, L, undefined_t> { \
|
||||
static char const* name() { return "__" #id "__"; } \
|
||||
static auto execute(const L &l) -> decltype(expr) { return expr; } \
|
||||
static B execute_cast(const L &l) { return B(expr); } \
|
||||
}; \
|
||||
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \
|
||||
return op_<op_##id, op_u, self_t, undefined_t>(); \
|
||||
}
|
||||
#define PYBIND11_UNARY_OPERATOR(id, op, expr) \
|
||||
template <typename B, typename L> \
|
||||
struct op_impl<op_##id, op_u, B, L, undefined_t> { \
|
||||
static char const *name() { return "__" #id "__"; } \
|
||||
static auto execute(const L &l) -> decltype(expr) { return expr; } \
|
||||
static B execute_cast(const L &l) { return B(expr); } \
|
||||
}; \
|
||||
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \
|
||||
return op_<op_##id, op_u, self_t, undefined_t>(); \
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r)
|
||||
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
|
||||
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r)
|
||||
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
|
||||
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
|
||||
PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
|
||||
PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
|
||||
PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r)
|
||||
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
|
||||
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
|
||||
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
|
||||
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
|
||||
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
|
||||
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
|
||||
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
|
||||
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
|
||||
//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
|
||||
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
|
||||
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
|
||||
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
|
||||
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
|
||||
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r)
|
||||
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
|
||||
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l *r)
|
||||
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
|
||||
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
|
||||
PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
|
||||
PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
|
||||
PYBIND11_BINARY_OPERATOR(and, rand, operator&, l &r)
|
||||
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
|
||||
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
|
||||
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
|
||||
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
|
||||
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
|
||||
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
|
||||
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
|
||||
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
|
||||
// PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
|
||||
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
|
||||
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
|
||||
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
|
||||
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
|
||||
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||
// WARNING: This usage of `abs` should only be done for existing STL overloads.
|
||||
// Adding overloads directly in to the `std::` namespace is advised against:
|
||||
// https://en.cppreference.com/w/cpp/language/extending_std
|
||||
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||
PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
|
||||
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
|
||||
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||
PYBIND11_UNARY_OPERATOR(int, int_, (int) l)
|
||||
PYBIND11_UNARY_OPERATOR(float, float_, (double) l)
|
||||
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||
PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
|
||||
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
|
||||
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||
PYBIND11_UNARY_OPERATOR(int, int_, (int) l)
|
||||
PYBIND11_UNARY_OPERATOR(float, float_, (double) l)
|
||||
|
||||
#undef PYBIND11_BINARY_OPERATOR
|
||||
#undef PYBIND11_INPLACE_OPERATOR
|
||||
|
||||
@ -15,44 +15,54 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
class options {
|
||||
public:
|
||||
|
||||
// Default RAII constructor, which leaves settings as they currently are.
|
||||
options() : previous_state(global_state()) {}
|
||||
|
||||
// Class is non-copyable.
|
||||
options(const options&) = delete;
|
||||
options& operator=(const options&) = delete;
|
||||
options(const options &) = delete;
|
||||
options &operator=(const options &) = delete;
|
||||
|
||||
// Destructor, which restores settings that were in effect before.
|
||||
~options() {
|
||||
global_state() = previous_state;
|
||||
}
|
||||
~options() { global_state() = previous_state; }
|
||||
|
||||
// Setter methods (affect the global state):
|
||||
|
||||
options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; }
|
||||
options &disable_user_defined_docstrings() & {
|
||||
global_state().show_user_defined_docstrings = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; }
|
||||
options &enable_user_defined_docstrings() & {
|
||||
global_state().show_user_defined_docstrings = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; }
|
||||
options &disable_function_signatures() & {
|
||||
global_state().show_function_signatures = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; }
|
||||
options &enable_function_signatures() & {
|
||||
global_state().show_function_signatures = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Getter methods (return the global state):
|
||||
|
||||
static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; }
|
||||
static bool show_user_defined_docstrings() {
|
||||
return global_state().show_user_defined_docstrings;
|
||||
}
|
||||
|
||||
static bool show_function_signatures() { return global_state().show_function_signatures; }
|
||||
|
||||
// This type is not meant to be allocated on the heap.
|
||||
void* operator new(size_t) = delete;
|
||||
void *operator new(size_t) = delete;
|
||||
|
||||
private:
|
||||
|
||||
struct state {
|
||||
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
|
||||
bool show_function_signatures = true; //< Include auto-generated function signatures
|
||||
// in docstrings.
|
||||
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
|
||||
bool show_function_signatures = true; //< Include auto-generated function signatures
|
||||
// in docstrings.
|
||||
};
|
||||
|
||||
static state &global_state() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -9,26 +9,27 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "pybind11.h"
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include "detail/common.h"
|
||||
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <valarray>
|
||||
|
||||
// See `detail/common.h` for implementation of these guards.
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
# include <optional>
|
||||
# include <optional>
|
||||
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
|
||||
# include <experimental/optional>
|
||||
# include <experimental/optional>
|
||||
#endif
|
||||
|
||||
#if defined(PYBIND11_HAS_VARIANT)
|
||||
# include <variant>
|
||||
# include <variant>
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
@ -37,8 +38,9 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for
|
||||
/// forwarding a container element). Typically used indirect via forwarded_type(), below.
|
||||
template <typename T, typename U>
|
||||
using forwarded_type = conditional_t<
|
||||
std::is_lvalue_reference<T>::value, remove_reference_t<U> &, remove_reference_t<U> &&>;
|
||||
using forwarded_type = conditional_t<std::is_lvalue_reference<T>::value,
|
||||
remove_reference_t<U> &,
|
||||
remove_reference_t<U> &&>;
|
||||
|
||||
/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically
|
||||
/// used for forwarding a container's elements.
|
||||
@ -47,7 +49,8 @@ forwarded_type<T, U> forward_like(U &&u) {
|
||||
return std::forward<detail::forwarded_type<T, U>>(std::forward<U>(u));
|
||||
}
|
||||
|
||||
template <typename Type, typename Key> struct set_caster {
|
||||
template <typename Type, typename Key>
|
||||
struct set_caster {
|
||||
using type = Type;
|
||||
using key_conv = make_caster<Key>;
|
||||
|
||||
@ -74,7 +77,8 @@ template <typename Type, typename Key> struct set_caster {
|
||||
}
|
||||
pybind11::set s;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(key_conv::cast(forward_like<T>(value), policy, parent));
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
key_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_ || !s.add(value_)) {
|
||||
return handle();
|
||||
}
|
||||
@ -85,8 +89,9 @@ template <typename Type, typename Key> struct set_caster {
|
||||
PYBIND11_TYPE_CASTER(type, const_name("Set[") + key_conv::name + const_name("]"));
|
||||
};
|
||||
|
||||
template <typename Type, typename Key, typename Value> struct map_caster {
|
||||
using key_conv = make_caster<Key>;
|
||||
template <typename Type, typename Key, typename Value>
|
||||
struct map_caster {
|
||||
using key_conv = make_caster<Key>;
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
@ -116,8 +121,10 @@ template <typename Type, typename Key, typename Value> struct map_caster {
|
||||
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));
|
||||
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));
|
||||
if (!key || !value) {
|
||||
return handle();
|
||||
}
|
||||
@ -126,10 +133,13 @@ template <typename Type, typename Key, typename Value> struct map_caster {
|
||||
return d.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type, const_name("Dict[") + key_conv::name + const_name(", ") + value_conv::name + const_name("]"));
|
||||
PYBIND11_TYPE_CASTER(Type,
|
||||
const_name("Dict[") + key_conv::name + const_name(", ") + value_conv::name
|
||||
+ const_name("]"));
|
||||
};
|
||||
|
||||
template <typename Type, typename Value> struct list_caster {
|
||||
template <typename Type, typename Value>
|
||||
struct list_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
@ -151,7 +161,7 @@ template <typename Type, typename Value> struct list_caster {
|
||||
|
||||
private:
|
||||
template <
|
||||
typename T = Type,
|
||||
typename T = Type,
|
||||
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
|
||||
void reserve_maybe(const sequence &s, Type *) {
|
||||
value.reserve(s.size());
|
||||
@ -167,7 +177,8 @@ public:
|
||||
list l(src.size());
|
||||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_) {
|
||||
return handle();
|
||||
}
|
||||
@ -179,16 +190,17 @@ public:
|
||||
PYBIND11_TYPE_CASTER(Type, const_name("List[") + value_conv::name + const_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::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::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> { };
|
||||
template <typename Type, typename Alloc>
|
||||
struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {};
|
||||
|
||||
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0> struct array_caster {
|
||||
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0>
|
||||
struct array_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
private:
|
||||
@ -229,7 +241,8 @@ public:
|
||||
list l(src.size());
|
||||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_) {
|
||||
return handle();
|
||||
}
|
||||
@ -238,29 +251,40 @@ public:
|
||||
return l.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(ArrayType, const_name("List[") + value_conv::name + const_name<Resizable>(const_name(""), const_name("[") + const_name<Size>() + const_name("]")) + const_name("]"));
|
||||
PYBIND11_TYPE_CASTER(ArrayType,
|
||||
const_name("List[") + value_conv::name
|
||||
+ const_name<Resizable>(const_name(""),
|
||||
const_name("[") + const_name<Size>()
|
||||
+ const_name("]"))
|
||||
+ const_name("]"));
|
||||
};
|
||||
|
||||
template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>>
|
||||
: array_caster<std::array<Type, Size>, Type, false, Size> { };
|
||||
template <typename Type, size_t Size>
|
||||
struct type_caster<std::array<Type, Size>>
|
||||
: array_caster<std::array<Type, Size>, Type, false, Size> {};
|
||||
|
||||
template <typename Type> struct type_caster<std::valarray<Type>>
|
||||
: array_caster<std::valarray<Type>, Type, true> { };
|
||||
template <typename Type>
|
||||
struct type_caster<std::valarray<Type>> : array_caster<std::valarray<Type>, Type, true> {};
|
||||
|
||||
template <typename Key, typename Compare, typename Alloc> struct type_caster<std::set<Key, Compare, Alloc>>
|
||||
: set_caster<std::set<Key, Compare, Alloc>, Key> { };
|
||||
template <typename Key, typename Compare, typename Alloc>
|
||||
struct type_caster<std::set<Key, Compare, Alloc>>
|
||||
: set_caster<std::set<Key, Compare, Alloc>, Key> {};
|
||||
|
||||
template <typename Key, typename Hash, typename Equal, typename Alloc> struct type_caster<std::unordered_set<Key, Hash, Equal, Alloc>>
|
||||
: set_caster<std::unordered_set<Key, Hash, Equal, Alloc>, Key> { };
|
||||
template <typename Key, typename Hash, typename Equal, typename Alloc>
|
||||
struct type_caster<std::unordered_set<Key, Hash, Equal, Alloc>>
|
||||
: set_caster<std::unordered_set<Key, Hash, Equal, Alloc>, Key> {};
|
||||
|
||||
template <typename Key, typename Value, typename Compare, typename Alloc> struct type_caster<std::map<Key, Value, Compare, Alloc>>
|
||||
: map_caster<std::map<Key, Value, Compare, Alloc>, Key, Value> { };
|
||||
template <typename Key, typename Value, typename Compare, typename Alloc>
|
||||
struct type_caster<std::map<Key, Value, Compare, Alloc>>
|
||||
: map_caster<std::map<Key, Value, Compare, Alloc>, Key, Value> {};
|
||||
|
||||
template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc> struct type_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>>
|
||||
: map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> { };
|
||||
template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc>
|
||||
struct type_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>>
|
||||
: map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> {};
|
||||
|
||||
// This type caster is intended to be used for std::optional and std::experimental::optional
|
||||
template<typename Type, typename Value = typename Type::value_type> struct optional_caster {
|
||||
template <typename Type, typename Value = typename Type::value_type>
|
||||
struct optional_caster {
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
template <typename T>
|
||||
@ -279,7 +303,7 @@ template<typename Type, typename Value = typename Type::value_type> struct optio
|
||||
return false;
|
||||
}
|
||||
if (src.is_none()) {
|
||||
return true; // default-constructed value is already empty
|
||||
return true; // default-constructed value is already empty
|
||||
}
|
||||
value_conv inner_caster;
|
||||
if (!inner_caster.load(src, convert)) {
|
||||
@ -294,18 +318,20 @@ template<typename Type, typename Value = typename Type::value_type> struct optio
|
||||
};
|
||||
|
||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||
template<typename T> struct type_caster<std::optional<T>>
|
||||
: public optional_caster<std::optional<T>> {};
|
||||
template <typename T>
|
||||
struct type_caster<std::optional<T>> : public optional_caster<std::optional<T>> {};
|
||||
|
||||
template<> struct type_caster<std::nullopt_t>
|
||||
: public void_caster<std::nullopt_t> {};
|
||||
template <>
|
||||
struct type_caster<std::nullopt_t> : public void_caster<std::nullopt_t> {};
|
||||
#endif
|
||||
|
||||
#if defined(PYBIND11_HAS_EXP_OPTIONAL)
|
||||
template<typename T> struct type_caster<std::experimental::optional<T>>
|
||||
template <typename T>
|
||||
struct type_caster<std::experimental::optional<T>>
|
||||
: public optional_caster<std::experimental::optional<T>> {};
|
||||
|
||||
template<> struct type_caster<std::experimental::nullopt_t>
|
||||
template <>
|
||||
struct type_caster<std::experimental::nullopt_t>
|
||||
: public void_caster<std::experimental::nullopt_t> {};
|
||||
#endif
|
||||
|
||||
@ -326,7 +352,7 @@ struct variant_caster_visitor {
|
||||
/// `namespace::variant` types which provide a `namespace::visit()` function are handled here
|
||||
/// automatically using argument-dependent lookup. Users can provide specializations for other
|
||||
/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`.
|
||||
template <template<typename...> class Variant>
|
||||
template <template <typename...> class Variant>
|
||||
struct visit_helper {
|
||||
template <typename... Args>
|
||||
static auto call(Args &&...args) -> decltype(visit(std::forward<Args>(args)...)) {
|
||||
@ -335,9 +361,10 @@ struct visit_helper {
|
||||
};
|
||||
|
||||
/// Generic variant caster
|
||||
template <typename Variant> struct variant_caster;
|
||||
template <typename Variant>
|
||||
struct variant_caster;
|
||||
|
||||
template <template<typename...> class V, typename... Ts>
|
||||
template <template <typename...> class V, typename... Ts>
|
||||
struct variant_caster<V<Ts...>> {
|
||||
static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative.");
|
||||
|
||||
@ -371,12 +398,14 @@ struct variant_caster<V<Ts...>> {
|
||||
}
|
||||
|
||||
using Type = V<Ts...>;
|
||||
PYBIND11_TYPE_CASTER(Type, const_name("Union[") + detail::concat(make_caster<Ts>::name...) + const_name("]"));
|
||||
PYBIND11_TYPE_CASTER(Type,
|
||||
const_name("Union[") + detail::concat(make_caster<Ts>::name...)
|
||||
+ const_name("]"));
|
||||
};
|
||||
|
||||
#if defined(PYBIND11_HAS_VARIANT)
|
||||
template <typename... Ts>
|
||||
struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> { };
|
||||
struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> {};
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
@ -4,21 +4,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../cast.h"
|
||||
#include "../pybind11.h"
|
||||
#include "../pytypes.h"
|
||||
|
||||
#include "../detail/common.h"
|
||||
#include "../detail/descr.h"
|
||||
#include "../cast.h"
|
||||
#include "../pytypes.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef __has_include
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \
|
||||
PY_VERSION_HEX >= 0x03060000
|
||||
# include <filesystem>
|
||||
# define PYBIND11_HAS_FILESYSTEM 1
|
||||
# endif
|
||||
# include <filesystem>
|
||||
# define PYBIND11_HAS_FILESYSTEM 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
|
||||
@ -30,28 +29,29 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if defined(PYBIND11_HAS_FILESYSTEM)
|
||||
template<typename T> struct path_caster {
|
||||
template <typename T>
|
||||
struct path_caster {
|
||||
|
||||
private:
|
||||
static PyObject* unicode_from_fs_native(const std::string& w) {
|
||||
#if !defined(PYPY_VERSION)
|
||||
static PyObject *unicode_from_fs_native(const std::string &w) {
|
||||
# if !defined(PYPY_VERSION)
|
||||
return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size()));
|
||||
#else
|
||||
# else
|
||||
// PyPy mistakenly declares the first parameter as non-const.
|
||||
return PyUnicode_DecodeFSDefaultAndSize(
|
||||
const_cast<char*>(w.c_str()), ssize_t(w.size()));
|
||||
#endif
|
||||
return PyUnicode_DecodeFSDefaultAndSize(const_cast<char *>(w.c_str()), ssize_t(w.size()));
|
||||
# endif
|
||||
}
|
||||
|
||||
static PyObject* unicode_from_fs_native(const std::wstring& w) {
|
||||
static PyObject *unicode_from_fs_native(const std::wstring &w) {
|
||||
return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size()));
|
||||
}
|
||||
|
||||
public:
|
||||
static handle cast(const T& path, return_value_policy, handle) {
|
||||
static handle cast(const T &path, return_value_policy, handle) {
|
||||
if (auto py_str = unicode_from_fs_native(path.native())) {
|
||||
return module_::import("pathlib").attr("Path")(reinterpret_steal<object>(py_str))
|
||||
.release();
|
||||
return module_::import("pathlib")
|
||||
.attr("Path")(reinterpret_steal<object>(py_str))
|
||||
.release();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -60,12 +60,12 @@ public:
|
||||
// PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of
|
||||
// calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy
|
||||
// issue #3168) so we do it ourselves instead.
|
||||
PyObject* buf = PyOS_FSPath(handle.ptr());
|
||||
PyObject *buf = PyOS_FSPath(handle.ptr());
|
||||
if (!buf) {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
PyObject* native = nullptr;
|
||||
PyObject *native = nullptr;
|
||||
if constexpr (std::is_same_v<typename T::value_type, char>) {
|
||||
if (PyUnicode_FSConverter(buf, &native) != 0) {
|
||||
if (auto *c_str = PyBytes_AsString(native)) {
|
||||
@ -78,7 +78,7 @@ public:
|
||||
if (PyUnicode_FSDecoder(buf, &native) != 0) {
|
||||
if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) {
|
||||
// AsWideCharString returns a new string that must be free'd.
|
||||
value = c_str; // Copies the string.
|
||||
value = c_str; // Copies the string.
|
||||
PyMem_Free(c_str);
|
||||
}
|
||||
}
|
||||
@ -95,8 +95,8 @@ public:
|
||||
PYBIND11_TYPE_CASTER(T, const_name("os.PathLike"));
|
||||
};
|
||||
|
||||
template<> struct type_caster<std::filesystem::path>
|
||||
: public path_caster<std::filesystem::path> {};
|
||||
template <>
|
||||
struct type_caster<std::filesystem::path> : public path_caster<std::filesystem::path> {};
|
||||
#endif // PYBIND11_HAS_FILESYSTEM
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
@ -19,74 +19,87 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/* SFINAE helper class used by 'is_comparable */
|
||||
template <typename T> struct container_traits {
|
||||
template <typename T2> static std::true_type test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>())*);
|
||||
template <typename T2> static std::false_type test_comparable(...);
|
||||
template <typename T2> static std::true_type test_value(typename T2::value_type *);
|
||||
template <typename T2> static std::false_type test_value(...);
|
||||
template <typename T2> static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *);
|
||||
template <typename T2> static std::false_type test_pair(...);
|
||||
template <typename T>
|
||||
struct container_traits {
|
||||
template <typename T2>
|
||||
static std::true_type
|
||||
test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>()) *);
|
||||
template <typename T2>
|
||||
static std::false_type test_comparable(...);
|
||||
template <typename T2>
|
||||
static std::true_type test_value(typename T2::value_type *);
|
||||
template <typename T2>
|
||||
static std::false_type test_value(...);
|
||||
template <typename T2>
|
||||
static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *);
|
||||
template <typename T2>
|
||||
static std::false_type test_pair(...);
|
||||
|
||||
static constexpr const bool is_comparable = std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value;
|
||||
static constexpr const bool is_pair = std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value;
|
||||
static constexpr const bool is_vector = std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value;
|
||||
static constexpr const bool is_comparable
|
||||
= std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value;
|
||||
static constexpr const bool is_pair
|
||||
= std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value;
|
||||
static constexpr const bool is_vector
|
||||
= std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value;
|
||||
static constexpr const bool is_element = !is_pair && !is_vector;
|
||||
};
|
||||
|
||||
/* Default: is_comparable -> std::false_type */
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct is_comparable : std::false_type { };
|
||||
struct is_comparable : std::false_type {};
|
||||
|
||||
/* For non-map data structures, check whether operator== can be instantiated */
|
||||
template <typename T>
|
||||
struct is_comparable<
|
||||
T, enable_if_t<container_traits<T>::is_element &&
|
||||
container_traits<T>::is_comparable>>
|
||||
: std::true_type { };
|
||||
T,
|
||||
enable_if_t<container_traits<T>::is_element && container_traits<T>::is_comparable>>
|
||||
: std::true_type {};
|
||||
|
||||
/* For a vector/map data structure, recursively check the value type
|
||||
(which is std::pair for maps) */
|
||||
template <typename T>
|
||||
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> {
|
||||
static constexpr const bool value =
|
||||
is_comparable<typename T::value_type>::value;
|
||||
static constexpr const bool value = is_comparable<typename T::value_type>::value;
|
||||
};
|
||||
|
||||
/* For pairs, recursively check the two data types */
|
||||
template <typename T>
|
||||
struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> {
|
||||
static constexpr const bool value =
|
||||
is_comparable<typename T::first_type>::value &&
|
||||
is_comparable<typename T::second_type>::value;
|
||||
static constexpr const bool value = is_comparable<typename T::first_type>::value
|
||||
&& is_comparable<typename T::second_type>::value;
|
||||
};
|
||||
|
||||
/* Fallback functions */
|
||||
template <typename, typename, typename... Args> void vector_if_copy_constructible(const Args &...) { }
|
||||
template <typename, typename, typename... Args> void vector_if_equal_operator(const Args &...) { }
|
||||
template <typename, typename, typename... Args> void vector_if_insertion_operator(const Args &...) { }
|
||||
template <typename, typename, typename... Args> void vector_modifiers(const Args &...) { }
|
||||
template <typename, typename, typename... Args>
|
||||
void vector_if_copy_constructible(const Args &...) {}
|
||||
template <typename, typename, typename... Args>
|
||||
void vector_if_equal_operator(const Args &...) {}
|
||||
template <typename, typename, typename... Args>
|
||||
void vector_if_insertion_operator(const Args &...) {}
|
||||
template <typename, typename, typename... Args>
|
||||
void vector_modifiers(const Args &...) {}
|
||||
|
||||
template<typename Vector, typename Class_>
|
||||
template <typename Vector, typename Class_>
|
||||
void vector_if_copy_constructible(enable_if_t<is_copy_constructible<Vector>::value, Class_> &cl) {
|
||||
cl.def(init<const Vector &>(), "Copy constructor");
|
||||
}
|
||||
|
||||
template<typename Vector, typename Class_>
|
||||
template <typename Vector, typename Class_>
|
||||
void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_> &cl) {
|
||||
using T = typename Vector::value_type;
|
||||
|
||||
cl.def(self == self);
|
||||
cl.def(self != self);
|
||||
|
||||
cl.def("count",
|
||||
[](const Vector &v, const T &x) {
|
||||
return std::count(v.begin(), v.end(), x);
|
||||
},
|
||||
cl.def(
|
||||
"count",
|
||||
[](const Vector &v, const T &x) { return std::count(v.begin(), v.end(), x); },
|
||||
arg("x"),
|
||||
"Return the number of times ``x`` appears in the list"
|
||||
);
|
||||
"Return the number of times ``x`` appears in the list");
|
||||
|
||||
cl.def("remove", [](Vector &v, const T &x) {
|
||||
cl.def(
|
||||
"remove",
|
||||
[](Vector &v, const T &x) {
|
||||
auto p = std::find(v.begin(), v.end(), x);
|
||||
if (p != v.end()) {
|
||||
v.erase(p);
|
||||
@ -96,23 +109,21 @@ void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_>
|
||||
},
|
||||
arg("x"),
|
||||
"Remove the first item from the list whose value is x. "
|
||||
"It is an error if there is no such item."
|
||||
);
|
||||
"It is an error if there is no such item.");
|
||||
|
||||
cl.def("__contains__",
|
||||
[](const Vector &v, const T &x) {
|
||||
return std::find(v.begin(), v.end(), x) != v.end();
|
||||
},
|
||||
cl.def(
|
||||
"__contains__",
|
||||
[](const Vector &v, const T &x) { return std::find(v.begin(), v.end(), x) != v.end(); },
|
||||
arg("x"),
|
||||
"Return true the container contains ``x``"
|
||||
);
|
||||
"Return true the container contains ``x``");
|
||||
}
|
||||
|
||||
// Vector modifiers -- requires a copyable vector_type:
|
||||
// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems
|
||||
// silly to allow deletion but not insertion, so include them here too.)
|
||||
// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it
|
||||
// seems silly to allow deletion but not insertion, so include them here too.)
|
||||
template <typename Vector, typename Class_>
|
||||
void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
|
||||
void vector_modifiers(
|
||||
enable_if_t<is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
|
||||
using T = typename Vector::value_type;
|
||||
using SizeType = typename Vector::size_type;
|
||||
using DiffType = typename Vector::difference_type;
|
||||
@ -127,10 +138,11 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
return i;
|
||||
};
|
||||
|
||||
cl.def("append",
|
||||
[](Vector &v, const T &value) { v.push_back(value); },
|
||||
arg("x"),
|
||||
"Add an item to the end of the list");
|
||||
cl.def(
|
||||
"append",
|
||||
[](Vector &v, const T &value) { v.push_back(value); },
|
||||
arg("x"),
|
||||
"Add an item to the end of the list");
|
||||
|
||||
cl.def(init([](const iterable &it) {
|
||||
auto v = std::unique_ptr<Vector>(new Vector());
|
||||
@ -141,20 +153,14 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
return v.release();
|
||||
}));
|
||||
|
||||
cl.def("clear",
|
||||
[](Vector &v) {
|
||||
v.clear();
|
||||
},
|
||||
"Clear the contents"
|
||||
);
|
||||
cl.def(
|
||||
"clear", [](Vector &v) { v.clear(); }, "Clear the contents");
|
||||
|
||||
cl.def("extend",
|
||||
[](Vector &v, const Vector &src) {
|
||||
v.insert(v.end(), src.begin(), src.end());
|
||||
},
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list"
|
||||
);
|
||||
cl.def(
|
||||
"extend",
|
||||
[](Vector &v, const Vector &src) { v.insert(v.end(), src.begin(), src.end()); },
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list");
|
||||
|
||||
cl.def(
|
||||
"extend",
|
||||
@ -179,7 +185,8 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
arg("L"),
|
||||
"Extend the list by appending all the items in the given list");
|
||||
|
||||
cl.def("insert",
|
||||
cl.def(
|
||||
"insert",
|
||||
[](Vector &v, DiffType i, const T &x) {
|
||||
// Can't use wrap_i; i == v.size() is OK
|
||||
if (i < 0) {
|
||||
@ -190,9 +197,9 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
}
|
||||
v.insert(v.begin() + i, x);
|
||||
},
|
||||
arg("i") , arg("x"),
|
||||
"Insert an item at a given position."
|
||||
);
|
||||
arg("i"),
|
||||
arg("x"),
|
||||
"Insert an item at a given position.");
|
||||
|
||||
cl.def(
|
||||
"pop",
|
||||
@ -206,7 +213,8 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
},
|
||||
"Remove and return the last item");
|
||||
|
||||
cl.def("pop",
|
||||
cl.def(
|
||||
"pop",
|
||||
[wrap_i](Vector &v, DiffType i) {
|
||||
i = wrap_i(i, v.size());
|
||||
T t = std::move(v[(SizeType) i]);
|
||||
@ -214,15 +222,12 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
return t;
|
||||
},
|
||||
arg("i"),
|
||||
"Remove and return the item at index ``i``"
|
||||
);
|
||||
"Remove and return the item at index ``i``");
|
||||
|
||||
cl.def("__setitem__",
|
||||
[wrap_i](Vector &v, DiffType i, const T &t) {
|
||||
i = wrap_i(i, v.size());
|
||||
v[(SizeType)i] = t;
|
||||
}
|
||||
);
|
||||
cl.def("__setitem__", [wrap_i](Vector &v, DiffType i, const T &t) {
|
||||
i = wrap_i(i, v.size());
|
||||
v[(SizeType) i] = t;
|
||||
});
|
||||
|
||||
/// Slicing protocol
|
||||
cl.def(
|
||||
@ -237,7 +242,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
auto *seq = new Vector();
|
||||
seq->reserve((size_t) slicelength);
|
||||
|
||||
for (size_t i=0; i<slicelength; ++i) {
|
||||
for (size_t i = 0; i < slicelength; ++i) {
|
||||
seq->push_back(v[start]);
|
||||
start += step;
|
||||
}
|
||||
@ -259,20 +264,20 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
"Left and right hand size of slice assignment have different sizes!");
|
||||
}
|
||||
|
||||
for (size_t i=0; i<slicelength; ++i) {
|
||||
for (size_t i = 0; i < slicelength; ++i) {
|
||||
v[start] = value[i];
|
||||
start += step;
|
||||
}
|
||||
},
|
||||
"Assign list elements using a slice object");
|
||||
|
||||
cl.def("__delitem__",
|
||||
cl.def(
|
||||
"__delitem__",
|
||||
[wrap_i](Vector &v, DiffType i) {
|
||||
i = wrap_i(i, v.size());
|
||||
v.erase(v.begin() + i);
|
||||
},
|
||||
"Delete the list elements at index ``i``"
|
||||
);
|
||||
"Delete the list elements at index ``i``");
|
||||
|
||||
cl.def(
|
||||
"__delitem__",
|
||||
@ -297,8 +302,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
|
||||
|
||||
// If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>),
|
||||
// we have to access by copying; otherwise we return by reference.
|
||||
template <typename Vector> using vector_needs_copy = negation<
|
||||
std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>>;
|
||||
template <typename Vector>
|
||||
using vector_needs_copy
|
||||
= negation<std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]),
|
||||
typename Vector::value_type &>>;
|
||||
|
||||
// The usual case: access and iterate by reference
|
||||
template <typename Vector, typename Class_>
|
||||
@ -306,7 +313,7 @@ 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;
|
||||
using ItType = typename Vector::iterator;
|
||||
|
||||
auto wrap_i = [](DiffType i, SizeType n) {
|
||||
if (i < 0) {
|
||||
@ -318,21 +325,22 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl)
|
||||
return i;
|
||||
};
|
||||
|
||||
cl.def("__getitem__",
|
||||
cl.def(
|
||||
"__getitem__",
|
||||
[wrap_i](Vector &v, DiffType i) -> T & {
|
||||
i = wrap_i(i, v.size());
|
||||
return v[(SizeType)i];
|
||||
return v[(SizeType) i];
|
||||
},
|
||||
return_value_policy::reference_internal // ref + keepalive
|
||||
);
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Vector &v) {
|
||||
return make_iterator<
|
||||
return_value_policy::reference_internal, ItType, ItType, T&>(
|
||||
v.begin(), v.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
cl.def(
|
||||
"__iter__",
|
||||
[](Vector &v) {
|
||||
return make_iterator<return_value_policy::reference_internal, ItType, ItType, T &>(
|
||||
v.begin(), v.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
);
|
||||
}
|
||||
|
||||
@ -342,7 +350,7 @@ 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;
|
||||
using ItType = typename Vector::iterator;
|
||||
cl.def("__getitem__", [](const Vector &v, DiffType i) -> T {
|
||||
if (i < 0 && (i += v.size()) < 0) {
|
||||
throw index_error();
|
||||
@ -353,25 +361,27 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl)
|
||||
return v[(SizeType) i];
|
||||
});
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Vector &v) {
|
||||
return make_iterator<
|
||||
return_value_policy::copy, ItType, ItType, T>(
|
||||
v.begin(), v.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
cl.def(
|
||||
"__iter__",
|
||||
[](Vector &v) {
|
||||
return make_iterator<return_value_policy::copy, ItType, ItType, T>(v.begin(), v.end());
|
||||
},
|
||||
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||
);
|
||||
}
|
||||
|
||||
template <typename Vector, typename Class_> auto vector_if_insertion_operator(Class_ &cl, std::string const &name)
|
||||
-> decltype(std::declval<std::ostream&>() << std::declval<typename Vector::value_type>(), void()) {
|
||||
template <typename Vector, typename Class_>
|
||||
auto vector_if_insertion_operator(Class_ &cl, std::string const &name)
|
||||
-> decltype(std::declval<std::ostream &>() << std::declval<typename Vector::value_type>(),
|
||||
void()) {
|
||||
using size_type = typename Vector::size_type;
|
||||
|
||||
cl.def("__repr__",
|
||||
[name](Vector &v) {
|
||||
cl.def(
|
||||
"__repr__",
|
||||
[name](Vector &v) {
|
||||
std::ostringstream s;
|
||||
s << name << '[';
|
||||
for (size_type i=0; i < v.size(); ++i) {
|
||||
for (size_type i = 0; i < v.size(); ++i) {
|
||||
s << v[i];
|
||||
if (i != v.size() - 1) {
|
||||
s << ", ";
|
||||
@ -380,8 +390,7 @@ template <typename Vector, typename Class_> auto vector_if_insertion_operator(Cl
|
||||
s << ']';
|
||||
return s.str();
|
||||
},
|
||||
"Return the canonical string representation of this list."
|
||||
);
|
||||
"Return the canonical string representation of this list.");
|
||||
}
|
||||
|
||||
// Provide the buffer interface for vectors if we have data() and we have a format for it
|
||||
@ -390,7 +399,11 @@ template <typename Vector, typename Class_> auto vector_if_insertion_operator(Cl
|
||||
template <typename Vector, typename = void>
|
||||
struct vector_has_data_and_format : std::false_type {};
|
||||
template <typename Vector>
|
||||
struct vector_has_data_and_format<Vector, enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(), std::declval<Vector>().data()), typename Vector::value_type*>::value>> : std::true_type {};
|
||||
struct vector_has_data_and_format<
|
||||
Vector,
|
||||
enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(),
|
||||
std::declval<Vector>().data()),
|
||||
typename Vector::value_type *>::value>> : std::true_type {};
|
||||
|
||||
// [workaround(intel)] Separate function required here
|
||||
// Workaround as the Intel compiler does not compile the enable_if_t part below
|
||||
@ -405,17 +418,23 @@ constexpr bool args_any_are_buffer() {
|
||||
|
||||
// Add the buffer interface to a vector
|
||||
template <typename Vector, typename Class_, typename... Args>
|
||||
void vector_buffer_impl(Class_& cl, std::true_type) {
|
||||
void vector_buffer_impl(Class_ &cl, std::true_type) {
|
||||
using T = typename Vector::value_type;
|
||||
|
||||
static_assert(vector_has_data_and_format<Vector>::value, "There is not an appropriate format descriptor for this vector");
|
||||
static_assert(vector_has_data_and_format<Vector>::value,
|
||||
"There is not an appropriate format descriptor for this vector");
|
||||
|
||||
// numpy.h declares this for arbitrary types, but it may raise an exception and crash hard
|
||||
// at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here
|
||||
format_descriptor<T>::format();
|
||||
|
||||
cl.def_buffer([](Vector& v) -> buffer_info {
|
||||
return buffer_info(v.data(), static_cast<ssize_t>(sizeof(T)), format_descriptor<T>::format(), 1, {v.size()}, {sizeof(T)});
|
||||
cl.def_buffer([](Vector &v) -> buffer_info {
|
||||
return buffer_info(v.data(),
|
||||
static_cast<ssize_t>(sizeof(T)),
|
||||
format_descriptor<T>::format(),
|
||||
1,
|
||||
{v.size()},
|
||||
{sizeof(T)});
|
||||
});
|
||||
|
||||
cl.def(init([](const buffer &buf) {
|
||||
@ -429,7 +448,7 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
|
||||
+ " C++: " + format_descriptor<T>::format() + ")");
|
||||
}
|
||||
|
||||
T *p = static_cast<T*>(info.ptr);
|
||||
T *p = static_cast<T *>(info.ptr);
|
||||
ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
|
||||
T *end = p + info.shape[0] * step;
|
||||
if (step == 1) {
|
||||
@ -441,18 +460,18 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
|
||||
vec.push_back(*p);
|
||||
}
|
||||
return vec;
|
||||
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename Vector, typename Class_, typename... Args>
|
||||
void vector_buffer_impl(Class_&, std::false_type) {}
|
||||
void vector_buffer_impl(Class_ &, std::false_type) {}
|
||||
|
||||
template <typename Vector, typename Class_, typename... Args>
|
||||
void vector_buffer(Class_& cl) {
|
||||
vector_buffer_impl<Vector, Class_, Args...>(cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{});
|
||||
void vector_buffer(Class_ &cl) {
|
||||
vector_buffer_impl<Vector, Class_, Args...>(
|
||||
cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{});
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
@ -461,7 +480,7 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
// std::vector
|
||||
//
|
||||
template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
|
||||
class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, Args&&... args) {
|
||||
class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, Args &&...args) {
|
||||
using Class_ = class_<Vector, holder_type>;
|
||||
|
||||
// If the value_type is unregistered (e.g. a converting type) or is itself registered
|
||||
@ -492,18 +511,13 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
|
||||
// Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive
|
||||
detail::vector_accessor<Vector, Class_>(cl);
|
||||
|
||||
cl.def("__bool__",
|
||||
[](const Vector &v) -> bool {
|
||||
return !v.empty();
|
||||
},
|
||||
"Check whether the list is nonempty"
|
||||
);
|
||||
cl.def(
|
||||
"__bool__",
|
||||
[](const Vector &v) -> bool { return !v.empty(); },
|
||||
"Check whether the list is nonempty");
|
||||
|
||||
cl.def("__len__", &Vector::size);
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
// C++ style functions deprecated, leaving it here as an example
|
||||
cl.def(init<size_type>());
|
||||
@ -547,8 +561,6 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// std::map, std::unordered_map
|
||||
//
|
||||
@ -556,55 +568,58 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/* Fallback functions */
|
||||
template <typename, typename, typename... Args> void map_if_insertion_operator(const Args &...) { }
|
||||
template <typename, typename, typename... Args> void map_assignment(const Args &...) { }
|
||||
template <typename, typename, typename... Args>
|
||||
void map_if_insertion_operator(const Args &...) {}
|
||||
template <typename, typename, typename... Args>
|
||||
void map_assignment(const Args &...) {}
|
||||
|
||||
// Map assignment when copy-assignable: just copy the value
|
||||
template <typename Map, typename Class_>
|
||||
void map_assignment(enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
|
||||
void map_assignment(
|
||||
enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Map &m, const KeyType &k, const MappedType &v) {
|
||||
auto it = m.find(k);
|
||||
if (it != m.end()) {
|
||||
it->second = v;
|
||||
} else {
|
||||
m.emplace(k, v);
|
||||
}
|
||||
}
|
||||
);
|
||||
cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
|
||||
auto it = m.find(k);
|
||||
if (it != m.end()) {
|
||||
it->second = v;
|
||||
} else {
|
||||
m.emplace(k, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting
|
||||
template<typename Map, typename Class_>
|
||||
void map_assignment(enable_if_t<
|
||||
!is_copy_assignable<typename Map::mapped_type>::value &&
|
||||
is_copy_constructible<typename Map::mapped_type>::value,
|
||||
Class_> &cl) {
|
||||
// Not copy-assignable, but still copy-constructible: we can update the value by erasing and
|
||||
// reinserting
|
||||
template <typename Map, typename Class_>
|
||||
void map_assignment(enable_if_t<!is_copy_assignable<typename Map::mapped_type>::value
|
||||
&& is_copy_constructible<typename Map::mapped_type>::value,
|
||||
Class_> &cl) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
|
||||
cl.def("__setitem__",
|
||||
[](Map &m, const KeyType &k, const MappedType &v) {
|
||||
// We can't use m[k] = v; because value type might not be default constructable
|
||||
auto r = m.emplace(k, v);
|
||||
if (!r.second) {
|
||||
// value type is not copy assignable so the only way to insert it is to erase it first...
|
||||
m.erase(r.first);
|
||||
m.emplace(k, v);
|
||||
}
|
||||
}
|
||||
);
|
||||
cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
|
||||
// We can't use m[k] = v; because value type might not be default constructable
|
||||
auto r = m.emplace(k, v);
|
||||
if (!r.second) {
|
||||
// value type is not copy assignable so the only way to insert it is to erase it
|
||||
// first...
|
||||
m.erase(r.first);
|
||||
m.emplace(k, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Map, typename Class_>
|
||||
auto map_if_insertion_operator(Class_ &cl, std::string const &name)
|
||||
-> decltype(std::declval<std::ostream &>() << std::declval<typename Map::key_type>()
|
||||
<< std::declval<typename Map::mapped_type>(),
|
||||
void()) {
|
||||
|
||||
template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &cl, std::string const &name)
|
||||
-> decltype(std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void()) {
|
||||
|
||||
cl.def("__repr__",
|
||||
[name](Map &m) {
|
||||
cl.def(
|
||||
"__repr__",
|
||||
[name](Map &m) {
|
||||
std::ostringstream s;
|
||||
s << name << '{';
|
||||
bool f = false;
|
||||
@ -618,32 +633,28 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
|
||||
s << '}';
|
||||
return s.str();
|
||||
},
|
||||
"Return the canonical string representation of this map."
|
||||
);
|
||||
"Return the canonical string representation of this map.");
|
||||
}
|
||||
|
||||
template<typename Map>
|
||||
struct keys_view
|
||||
{
|
||||
template <typename Map>
|
||||
struct keys_view {
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template<typename Map>
|
||||
struct values_view
|
||||
{
|
||||
template <typename Map>
|
||||
struct values_view {
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template<typename Map>
|
||||
struct items_view
|
||||
{
|
||||
template <typename Map>
|
||||
struct items_view {
|
||||
Map ↦
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
|
||||
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&... args) {
|
||||
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&...args) {
|
||||
using KeyType = typename Map::key_type;
|
||||
using MappedType = typename Map::mapped_type;
|
||||
using KeysView = detail::keys_view<Map>;
|
||||
@ -674,101 +685,97 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
|
||||
// Register stream insertion operator (if possible)
|
||||
detail::map_if_insertion_operator<Map, Class_>(cl, name);
|
||||
|
||||
cl.def("__bool__",
|
||||
cl.def(
|
||||
"__bool__",
|
||||
[](const Map &m) -> bool { return !m.empty(); },
|
||||
"Check whether the map is nonempty"
|
||||
"Check whether the map is nonempty");
|
||||
|
||||
cl.def(
|
||||
"__iter__",
|
||||
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
|
||||
);
|
||||
|
||||
cl.def("__iter__",
|
||||
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
|
||||
cl.def(
|
||||
"keys",
|
||||
[](Map &m) { return KeysView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("keys",
|
||||
[](Map &m) { return KeysView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
cl.def(
|
||||
"values",
|
||||
[](Map &m) { return ValuesView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("values",
|
||||
[](Map &m) { return ValuesView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
cl.def(
|
||||
"items",
|
||||
[](Map &m) { return ItemsView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("items",
|
||||
[](Map &m) { return ItemsView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def("__getitem__",
|
||||
cl.def(
|
||||
"__getitem__",
|
||||
[](Map &m, const KeyType &k) -> MappedType & {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end()) {
|
||||
throw key_error();
|
||||
}
|
||||
return it->second;
|
||||
return it->second;
|
||||
},
|
||||
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;
|
||||
cl.def("__contains__", [](Map &m, const KeyType &k) -> bool {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end()) {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
return true;
|
||||
});
|
||||
// Fallback for when the object is not of the key type
|
||||
cl.def("__contains__", [](Map &, const object &) -> bool { return false; });
|
||||
|
||||
// Assignment provided only if the type is copyable
|
||||
detail::map_assignment<Map, Class_>(cl);
|
||||
|
||||
cl.def("__delitem__",
|
||||
[](Map &m, const KeyType &k) {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end()) {
|
||||
throw key_error();
|
||||
}
|
||||
m.erase(it);
|
||||
}
|
||||
);
|
||||
cl.def("__delitem__", [](Map &m, const KeyType &k) {
|
||||
auto it = m.find(k);
|
||||
if (it == m.end()) {
|
||||
throw key_error();
|
||||
}
|
||||
m.erase(it);
|
||||
});
|
||||
|
||||
cl.def("__len__", &Map::size);
|
||||
|
||||
keys_view.def("__len__", [](KeysView &view) { return view.map.size(); });
|
||||
keys_view.def("__iter__",
|
||||
[](KeysView &view) {
|
||||
return make_key_iterator(view.map.begin(), view.map.end());
|
||||
},
|
||||
keys_view.def(
|
||||
"__iter__",
|
||||
[](KeysView &view) { return make_key_iterator(view.map.begin(), view.map.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
keys_view.def("__contains__",
|
||||
[](KeysView &view, const KeyType &k) -> bool {
|
||||
auto it = view.map.find(k);
|
||||
if (it == view.map.end()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
keys_view.def("__contains__", [](KeysView &view, const KeyType &k) -> bool {
|
||||
auto it = view.map.find(k);
|
||||
if (it == view.map.end()) {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
return true;
|
||||
});
|
||||
// Fallback for when the object is not of the key type
|
||||
keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; });
|
||||
|
||||
values_view.def("__len__", [](ValuesView &view) { return view.map.size(); });
|
||||
values_view.def("__iter__",
|
||||
[](ValuesView &view) {
|
||||
return make_value_iterator(view.map.begin(), view.map.end());
|
||||
},
|
||||
values_view.def(
|
||||
"__iter__",
|
||||
[](ValuesView &view) { return make_value_iterator(view.map.begin(), view.map.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
|
||||
items_view.def("__len__", [](ItemsView &view) { return view.map.size(); });
|
||||
items_view.def("__iter__",
|
||||
[](ItemsView &view) {
|
||||
return make_iterator(view.map.begin(), view.map.end());
|
||||
},
|
||||
items_view.def(
|
||||
"__iter__",
|
||||
[](ItemsView &view) { return make_iterator(view.map.begin(), view.map.end()); },
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
|
||||
|
||||
@ -56,7 +56,8 @@ from the ConstructorStats instance `.values()` method.
|
||||
In some cases, when you need to track instances of a C++ class not registered with pybind11, you
|
||||
need to add a function returning the ConstructorStats for the C++ class; this can be done with:
|
||||
|
||||
m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>, py::return_value_policy::reference)
|
||||
m.def("get_special_cstats", &ConstructorStats::get<SpecialClass>,
|
||||
py::return_value_policy::reference)
|
||||
|
||||
Finally, you can suppress the output messages, but keep the constructor tracking (for
|
||||
inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g.
|
||||
@ -65,17 +66,18 @@ inspection/testing in python) by using the functions with `print_` replaced with
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#include <list>
|
||||
#include <typeindex>
|
||||
#include <sstream>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
|
||||
class ConstructorStats {
|
||||
protected:
|
||||
std::unordered_map<void*, int> _instances; // Need a map rather than set because members can
|
||||
// shared address with parents
|
||||
std::list<std::string> _values; // Used to track values
|
||||
// (e.g. of value constructors)
|
||||
std::unordered_map<void *, int> _instances; // Need a map rather than set because members can
|
||||
// shared address with parents
|
||||
std::list<std::string> _values; // Used to track values
|
||||
// (e.g. of value constructors)
|
||||
public:
|
||||
int default_constructions = 0;
|
||||
int copy_constructions = 0;
|
||||
@ -98,9 +100,7 @@ public:
|
||||
default_constructions++;
|
||||
}
|
||||
|
||||
void created(void *inst) {
|
||||
++_instances[inst];
|
||||
}
|
||||
void created(void *inst) { ++_instances[inst]; }
|
||||
|
||||
void destroyed(void *inst) {
|
||||
if (--_instances[inst] < 0) {
|
||||
@ -114,11 +114,12 @@ public:
|
||||
// Force garbage collection to ensure any pending destructors are invoked:
|
||||
#if defined(PYPY_VERSION)
|
||||
PyObject *globals = PyEval_GetGlobals();
|
||||
PyObject *result = PyRun_String(
|
||||
"import gc\n"
|
||||
"for i in range(2):"
|
||||
" gc.collect()\n",
|
||||
Py_file_input, globals, globals);
|
||||
PyObject *result = PyRun_String("import gc\n"
|
||||
"for i in range(2):"
|
||||
" gc.collect()\n",
|
||||
Py_file_input,
|
||||
globals,
|
||||
globals);
|
||||
if (result == nullptr)
|
||||
throw py::error_already_set();
|
||||
Py_DECREF(result);
|
||||
@ -140,7 +141,8 @@ public:
|
||||
|
||||
void value() {} // Recursion terminator
|
||||
// Takes one or more values, converts them to strings, then stores them.
|
||||
template <typename T, typename... Tmore> void value(const T &v, Tmore &&...args) {
|
||||
template <typename T, typename... Tmore>
|
||||
void value(const T &v, Tmore &&...args) {
|
||||
std::ostringstream oss;
|
||||
oss << v;
|
||||
_values.push_back(oss.str());
|
||||
@ -158,13 +160,14 @@ public:
|
||||
}
|
||||
|
||||
// Gets constructor stats from a C++ type index
|
||||
static ConstructorStats& get(std::type_index type) {
|
||||
static ConstructorStats &get(std::type_index type) {
|
||||
static std::unordered_map<std::type_index, ConstructorStats> all_cstats;
|
||||
return all_cstats[type];
|
||||
}
|
||||
|
||||
// Gets constructor stats from a C++ type
|
||||
template <typename T> static ConstructorStats& get() {
|
||||
template <typename T>
|
||||
static ConstructorStats &get() {
|
||||
#if defined(PYPY_VERSION)
|
||||
gc();
|
||||
#endif
|
||||
@ -172,11 +175,12 @@ public:
|
||||
}
|
||||
|
||||
// Gets constructor stats from a Python class
|
||||
static ConstructorStats& get(py::object class_) {
|
||||
static ConstructorStats &get(py::object class_) {
|
||||
auto &internals = py::detail::get_internals();
|
||||
const std::type_index *t1 = nullptr, *t2 = nullptr;
|
||||
try {
|
||||
auto *type_info = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0);
|
||||
auto *type_info
|
||||
= internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0);
|
||||
for (auto &p : internals.registered_types_cpp) {
|
||||
if (p.second == type_info) {
|
||||
if (t1) {
|
||||
@ -186,18 +190,20 @@ public:
|
||||
t1 = &p.first;
|
||||
}
|
||||
}
|
||||
} catch (const std::out_of_range &) {
|
||||
}
|
||||
catch (const std::out_of_range&) {}
|
||||
if (!t1) {
|
||||
throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
|
||||
}
|
||||
auto &cs1 = get(*t1);
|
||||
// If we have both a t1 and t2 match, one is probably the trampoline class; return whichever
|
||||
// has more constructions (typically one or the other will be 0)
|
||||
// If we have both a t1 and t2 match, one is probably the trampoline class; return
|
||||
// whichever has more constructions (typically one or the other will be 0)
|
||||
if (t2) {
|
||||
auto &cs2 = get(*t2);
|
||||
int cs1_total = cs1.default_constructions + cs1.copy_constructions + cs1.move_constructions + (int) cs1._values.size();
|
||||
int cs2_total = cs2.default_constructions + cs2.copy_constructions + cs2.move_constructions + (int) cs2._values.size();
|
||||
int cs1_total = cs1.default_constructions + cs1.copy_constructions
|
||||
+ cs1.move_constructions + (int) cs1._values.size();
|
||||
int cs2_total = cs2.default_constructions + cs2.copy_constructions
|
||||
+ cs2.move_constructions + (int) cs2._values.size();
|
||||
if (cs2_total > cs1_total) {
|
||||
return cs2;
|
||||
}
|
||||
@ -209,78 +215,108 @@ public:
|
||||
// To track construction/destruction, you need to call these methods from the various
|
||||
// constructors/operators. The ones that take extra values record the given values in the
|
||||
// constructor stats values for later inspection.
|
||||
template <class T> void track_copy_created(T *inst) { ConstructorStats::get<T>().copy_created(inst); }
|
||||
template <class T> void track_move_created(T *inst) { ConstructorStats::get<T>().move_created(inst); }
|
||||
template <class T, typename... Values> void track_copy_assigned(T *, Values &&...values) {
|
||||
template <class T>
|
||||
void track_copy_created(T *inst) {
|
||||
ConstructorStats::get<T>().copy_created(inst);
|
||||
}
|
||||
template <class T>
|
||||
void track_move_created(T *inst) {
|
||||
ConstructorStats::get<T>().move_created(inst);
|
||||
}
|
||||
template <class T, typename... Values>
|
||||
void track_copy_assigned(T *, Values &&...values) {
|
||||
auto &cst = ConstructorStats::get<T>();
|
||||
cst.copy_assignments++;
|
||||
cst.value(std::forward<Values>(values)...);
|
||||
}
|
||||
template <class T, typename... Values> void track_move_assigned(T *, Values &&...values) {
|
||||
template <class T, typename... Values>
|
||||
void track_move_assigned(T *, Values &&...values) {
|
||||
auto &cst = ConstructorStats::get<T>();
|
||||
cst.move_assignments++;
|
||||
cst.value(std::forward<Values>(values)...);
|
||||
}
|
||||
template <class T, typename... Values> void track_default_created(T *inst, Values &&...values) {
|
||||
template <class T, typename... Values>
|
||||
void track_default_created(T *inst, Values &&...values) {
|
||||
auto &cst = ConstructorStats::get<T>();
|
||||
cst.default_created(inst);
|
||||
cst.value(std::forward<Values>(values)...);
|
||||
}
|
||||
template <class T, typename... Values> void track_created(T *inst, Values &&...values) {
|
||||
template <class T, typename... Values>
|
||||
void track_created(T *inst, Values &&...values) {
|
||||
auto &cst = ConstructorStats::get<T>();
|
||||
cst.created(inst);
|
||||
cst.value(std::forward<Values>(values)...);
|
||||
}
|
||||
template <class T, typename... Values> void track_destroyed(T *inst) {
|
||||
template <class T, typename... Values>
|
||||
void track_destroyed(T *inst) {
|
||||
ConstructorStats::get<T>().destroyed(inst);
|
||||
}
|
||||
template <class T, typename... Values> void track_values(T *, Values &&...values) {
|
||||
template <class T, typename... Values>
|
||||
void track_values(T *, Values &&...values) {
|
||||
ConstructorStats::get<T>().value(std::forward<Values>(values)...);
|
||||
}
|
||||
|
||||
/// Don't cast pointers to Python, print them as strings
|
||||
inline const char *format_ptrs(const char *p) { return p; }
|
||||
template <typename T>
|
||||
py::str format_ptrs(T *p) { return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p)); }
|
||||
py::str format_ptrs(T *p) {
|
||||
return "{:#x}"_s.format(reinterpret_cast<std::uintptr_t>(p));
|
||||
}
|
||||
template <typename T>
|
||||
auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) { return std::forward<T>(x); }
|
||||
auto format_ptrs(T &&x) -> decltype(std::forward<T>(x)) {
|
||||
return std::forward<T>(x);
|
||||
}
|
||||
|
||||
template <class T, typename... Output>
|
||||
void print_constr_details(T *inst, const std::string &action, Output &&...output) {
|
||||
py::print("###", py::type_id<T>(), "@", format_ptrs(inst), action,
|
||||
py::print("###",
|
||||
py::type_id<T>(),
|
||||
"@",
|
||||
format_ptrs(inst),
|
||||
action,
|
||||
format_ptrs(std::forward<Output>(output))...);
|
||||
}
|
||||
|
||||
// Verbose versions of the above:
|
||||
template <class T, typename... Values> void print_copy_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values
|
||||
template <class T, typename... Values>
|
||||
void print_copy_created(T *inst,
|
||||
Values &&...values) { // NB: this prints, but doesn't store, given values
|
||||
print_constr_details(inst, "created via copy constructor", values...);
|
||||
track_copy_created(inst);
|
||||
}
|
||||
template <class T, typename... Values> void print_move_created(T *inst, Values &&...values) { // NB: this prints, but doesn't store, given values
|
||||
template <class T, typename... Values>
|
||||
void print_move_created(T *inst,
|
||||
Values &&...values) { // NB: this prints, but doesn't store, given values
|
||||
print_constr_details(inst, "created via move constructor", values...);
|
||||
track_move_created(inst);
|
||||
}
|
||||
template <class T, typename... Values> void print_copy_assigned(T *inst, Values &&...values) {
|
||||
template <class T, typename... Values>
|
||||
void print_copy_assigned(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, "assigned via copy assignment", values...);
|
||||
track_copy_assigned(inst, values...);
|
||||
}
|
||||
template <class T, typename... Values> void print_move_assigned(T *inst, Values &&...values) {
|
||||
template <class T, typename... Values>
|
||||
void print_move_assigned(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, "assigned via move assignment", values...);
|
||||
track_move_assigned(inst, values...);
|
||||
}
|
||||
template <class T, typename... Values> void print_default_created(T *inst, Values &&...values) {
|
||||
template <class T, typename... Values>
|
||||
void print_default_created(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, "created via default constructor", values...);
|
||||
track_default_created(inst, values...);
|
||||
}
|
||||
template <class T, typename... Values> void print_created(T *inst, Values &&...values) {
|
||||
template <class T, typename... Values>
|
||||
void print_created(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, "created", values...);
|
||||
track_created(inst, values...);
|
||||
}
|
||||
template <class T, typename... Values> void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values
|
||||
template <class T, typename... Values>
|
||||
void print_destroyed(T *inst, Values &&...values) { // Prints but doesn't store given values
|
||||
print_constr_details(inst, "destroyed", values...);
|
||||
track_destroyed(inst);
|
||||
}
|
||||
template <class T, typename... Values> void print_values(T *inst, Values &&...values) {
|
||||
template <class T, typename... Values>
|
||||
void print_values(T *inst, Values &&...values) {
|
||||
print_constr_details(inst, ":", values...);
|
||||
track_values(inst, values...);
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// This file mimics a DSO that makes pybind11 calls but does not define a
|
||||
@ -25,34 +26,25 @@ void gil_acquire() { py::gil_scoped_acquire gil; }
|
||||
constexpr char kModuleName[] = "cross_module_gil_utils";
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
kModuleName,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
struct PyModuleDef moduledef
|
||||
= {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL};
|
||||
#else
|
||||
PyMethodDef module_methods[] = {
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
PyMethodDef module_methods[] = {{NULL, NULL, 0, NULL}};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
extern "C" PYBIND11_EXPORT
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject* PyInit_cross_module_gil_utils()
|
||||
PyObject *
|
||||
PyInit_cross_module_gil_utils()
|
||||
#else
|
||||
void initcross_module_gil_utils()
|
||||
void
|
||||
initcross_module_gil_utils()
|
||||
#endif
|
||||
{
|
||||
|
||||
PyObject* m =
|
||||
PyObject *m =
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyModule_Create(&moduledef);
|
||||
#else
|
||||
@ -60,11 +52,10 @@ void initcross_module_gil_utils()
|
||||
#endif
|
||||
|
||||
if (m != NULL) {
|
||||
static_assert(
|
||||
sizeof(&gil_acquire) == sizeof(void*),
|
||||
"Function pointer must have the same size as void*");
|
||||
PyModule_AddObject(m, "gil_acquire_funcaddr",
|
||||
PyLong_FromVoidPtr(reinterpret_cast<void*>(&gil_acquire)));
|
||||
static_assert(sizeof(&gil_acquire) == sizeof(void *),
|
||||
"Function pointer must have the same size as void*");
|
||||
PyModule_AddObject(
|
||||
m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire)));
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
#include <utility>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
/// Simple class used to test py::local:
|
||||
template <int> class LocalBase {
|
||||
template <int>
|
||||
class LocalBase {
|
||||
public:
|
||||
explicit LocalBase(int i) : i(i) { }
|
||||
explicit LocalBase(int i) : i(i) {}
|
||||
int i = -1;
|
||||
};
|
||||
|
||||
@ -35,12 +36,12 @@ using NonLocalVec2 = std::vector<NonLocal2>;
|
||||
using NonLocalMap = std::unordered_map<std::string, NonLocalType>;
|
||||
using NonLocalMap2 = std::unordered_map<std::string, uint8_t>;
|
||||
|
||||
|
||||
// Exception that will be caught via the module local translator.
|
||||
class LocalException : public std::exception {
|
||||
public:
|
||||
explicit LocalException(const char * m) : message{m} {}
|
||||
const char * what() const noexcept override {return message.c_str();}
|
||||
explicit LocalException(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
@ -48,8 +49,9 @@ private:
|
||||
// Exception that will be registered with register_local_exception_translator
|
||||
class LocalSimpleException : public std::exception {
|
||||
public:
|
||||
explicit LocalSimpleException(const char * m) : message{m} {}
|
||||
const char * what() const noexcept override {return message.c_str();}
|
||||
explicit LocalSimpleException(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
@ -58,17 +60,16 @@ PYBIND11_MAKE_OPAQUE(LocalVec);
|
||||
PYBIND11_MAKE_OPAQUE(LocalVec2);
|
||||
PYBIND11_MAKE_OPAQUE(LocalMap);
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalVec);
|
||||
//PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2
|
||||
// PYBIND11_MAKE_OPAQUE(NonLocalVec2); // same type as LocalVec2
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalMap);
|
||||
PYBIND11_MAKE_OPAQUE(NonLocalMap2);
|
||||
|
||||
|
||||
// Simple bindings (used with the above):
|
||||
template <typename T, int Adjust = 0, typename... Args>
|
||||
py::class_<T> bind_local(Args && ...args) {
|
||||
return py::class_<T>(std::forward<Args>(args)...)
|
||||
.def(py::init<int>())
|
||||
.def("get", [](T &i) { return i.i + Adjust; });
|
||||
py::class_<T> bind_local(Args &&...args) {
|
||||
return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) {
|
||||
return i.i + Adjust;
|
||||
});
|
||||
};
|
||||
|
||||
// Simulate a foreign library base class (to match the example in the docs):
|
||||
@ -81,5 +82,11 @@ public:
|
||||
};
|
||||
} // namespace pets
|
||||
|
||||
struct MixGL { int i; explicit MixGL(int i) : i{i} {} };
|
||||
struct MixGL2 { int i; explicit MixGL2(int i) : i{i} {} };
|
||||
struct MixGL {
|
||||
int i;
|
||||
explicit MixGL(int i) : i{i} {}
|
||||
};
|
||||
struct MixGL2 {
|
||||
int i;
|
||||
explicit MixGL2(int i) : i{i} {}
|
||||
};
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
#if !defined(__OBJECT_H)
|
||||
#define __OBJECT_H
|
||||
# define __OBJECT_H
|
||||
|
||||
#include <atomic>
|
||||
#include "constructor_stats.h"
|
||||
# include "constructor_stats.h"
|
||||
|
||||
# include <atomic>
|
||||
|
||||
/// Reference counted object base class
|
||||
class Object {
|
||||
@ -35,13 +36,15 @@ public:
|
||||
}
|
||||
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
protected:
|
||||
/** \brief Virtual protected deconstructor.
|
||||
* (Will only be called by \ref ref)
|
||||
*/
|
||||
virtual ~Object() { print_destroyed(this); }
|
||||
|
||||
private:
|
||||
mutable std::atomic<int> m_refCount { 0 };
|
||||
mutable std::atomic<int> m_refCount{0};
|
||||
};
|
||||
|
||||
// Tag class used to track constructions of ref objects. When we track constructors, below, we
|
||||
@ -60,10 +63,14 @@ class ref_tag {};
|
||||
*
|
||||
* \ingroup libcore
|
||||
*/
|
||||
template <typename T> class ref {
|
||||
template <typename T>
|
||||
class ref {
|
||||
public:
|
||||
/// Create a nullptr reference
|
||||
ref() : m_ptr(nullptr) { print_default_created(this); track_default_created((ref_tag*) this); }
|
||||
ref() : m_ptr(nullptr) {
|
||||
print_default_created(this);
|
||||
track_default_created((ref_tag *) this);
|
||||
}
|
||||
|
||||
/// Construct a reference from a pointer
|
||||
explicit ref(T *ptr) : m_ptr(ptr) {
|
||||
@ -71,8 +78,8 @@ public:
|
||||
((Object *) m_ptr)->incRef();
|
||||
}
|
||||
|
||||
print_created(this, "from pointer", m_ptr); track_created((ref_tag*) this, "from pointer");
|
||||
|
||||
print_created(this, "from pointer", m_ptr);
|
||||
track_created((ref_tag *) this, "from pointer");
|
||||
}
|
||||
|
||||
/// Copy constructor
|
||||
@ -81,14 +88,16 @@ public:
|
||||
((Object *) m_ptr)->incRef();
|
||||
}
|
||||
|
||||
print_copy_created(this, "with pointer", m_ptr); track_copy_created((ref_tag*) this);
|
||||
print_copy_created(this, "with pointer", m_ptr);
|
||||
track_copy_created((ref_tag *) this);
|
||||
}
|
||||
|
||||
/// Move constructor
|
||||
ref(ref &&r) noexcept : m_ptr(r.m_ptr) {
|
||||
r.m_ptr = nullptr;
|
||||
|
||||
print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this);
|
||||
print_move_created(this, "with pointer", m_ptr);
|
||||
track_move_created((ref_tag *) this);
|
||||
}
|
||||
|
||||
/// Destroy this reference
|
||||
@ -97,12 +106,14 @@ public:
|
||||
((Object *) m_ptr)->decRef();
|
||||
}
|
||||
|
||||
print_destroyed(this); track_destroyed((ref_tag*) this);
|
||||
print_destroyed(this);
|
||||
track_destroyed((ref_tag *) this);
|
||||
}
|
||||
|
||||
/// Move another reference into the current one
|
||||
ref &operator=(ref &&r) noexcept {
|
||||
print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this);
|
||||
print_move_assigned(this, "pointer", r.m_ptr);
|
||||
track_move_assigned((ref_tag *) this);
|
||||
|
||||
if (*this == r) {
|
||||
return *this;
|
||||
@ -116,7 +127,7 @@ public:
|
||||
}
|
||||
|
||||
/// Overwrite this reference with another reference
|
||||
ref& operator=(const ref& r) {
|
||||
ref &operator=(const ref &r) {
|
||||
if (this == &r) {
|
||||
return *this;
|
||||
}
|
||||
@ -137,8 +148,9 @@ public:
|
||||
}
|
||||
|
||||
/// Overwrite this reference with a pointer to another object
|
||||
ref& operator=(T *ptr) {
|
||||
print_values(this, "assigned pointer"); track_values((ref_tag*) this, "assigned pointer");
|
||||
ref &operator=(T *ptr) {
|
||||
print_values(this, "assigned pointer");
|
||||
track_values((ref_tag *) this, "assigned pointer");
|
||||
|
||||
if (m_ptr == ptr) {
|
||||
return *this;
|
||||
@ -160,31 +172,32 @@ public:
|
||||
bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; }
|
||||
|
||||
/// Compare this reference with a pointer
|
||||
bool operator==(const T* ptr) const { return m_ptr == ptr; }
|
||||
bool operator==(const T *ptr) const { return m_ptr == ptr; }
|
||||
|
||||
/// Compare this reference with a pointer
|
||||
bool operator!=(const T* ptr) const { return m_ptr != ptr; }
|
||||
bool operator!=(const T *ptr) const { return m_ptr != ptr; }
|
||||
|
||||
/// Access the object referenced by this reference
|
||||
T* operator->() { return m_ptr; }
|
||||
T *operator->() { return m_ptr; }
|
||||
|
||||
/// Access the object referenced by this reference
|
||||
const T* operator->() const { return m_ptr; }
|
||||
const T *operator->() const { return m_ptr; }
|
||||
|
||||
/// Return a C++ reference to the referenced object
|
||||
T& operator*() { return *m_ptr; }
|
||||
T &operator*() { return *m_ptr; }
|
||||
|
||||
/// Return a const C++ reference to the referenced object
|
||||
const T& operator*() const { return *m_ptr; }
|
||||
const T &operator*() const { return *m_ptr; }
|
||||
|
||||
/// Return a pointer to the referenced object
|
||||
explicit operator T* () { return m_ptr; }
|
||||
explicit operator T *() { return m_ptr; }
|
||||
|
||||
/// Return a const pointer to the referenced object
|
||||
T* get_ptr() { return m_ptr; }
|
||||
T *get_ptr() { return m_ptr; }
|
||||
|
||||
/// Return a pointer to the referenced object
|
||||
const T* get_ptr() const { return m_ptr; }
|
||||
const T *get_ptr() const { return m_ptr; }
|
||||
|
||||
private:
|
||||
T *m_ptr;
|
||||
};
|
||||
|
||||
@ -7,12 +7,12 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "local_bindings.h"
|
||||
#include "test_exceptions.h"
|
||||
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
#include "local_bindings.h"
|
||||
#include "pybind11_tests.h"
|
||||
#include "test_exceptions.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
@ -30,39 +30,45 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
|
||||
// test_exceptions.py
|
||||
py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException");
|
||||
m.def("raise_runtime_error", []() { PyErr_SetString(PyExc_RuntimeError, "My runtime error"); throw py::error_already_set(); });
|
||||
m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); });
|
||||
m.def("raise_runtime_error", []() {
|
||||
PyErr_SetString(PyExc_RuntimeError, "My runtime error");
|
||||
throw py::error_already_set();
|
||||
});
|
||||
m.def("raise_value_error", []() {
|
||||
PyErr_SetString(PyExc_ValueError, "My value error");
|
||||
throw py::error_already_set();
|
||||
});
|
||||
m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); });
|
||||
m.def("throw_pybind_type_error", []() { throw py::type_error("pybind11 type error"); });
|
||||
m.def("throw_stop_iteration", []() { throw py::stop_iteration(); });
|
||||
m.def("throw_local_error", []() { throw LocalException("just local"); });
|
||||
m.def("throw_local_simple_error", []() { throw LocalSimpleException("external mod"); });
|
||||
py::register_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const shared_exception &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const shared_exception &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// translate the local exception into a key error but only in this module
|
||||
py::register_local_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const LocalException &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const LocalException &e) {
|
||||
PyErr_SetString(PyExc_KeyError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
// test_local_bindings.py
|
||||
// Local to both:
|
||||
bind_local<LocalType, 1>(m, "LocalType", py::module_local())
|
||||
.def("get2", [](LocalType &t) { return t.i + 2; })
|
||||
;
|
||||
bind_local<LocalType, 1>(m, "LocalType", py::module_local()).def("get2", [](LocalType &t) {
|
||||
return t.i + 2;
|
||||
});
|
||||
|
||||
// Can only be called with our python type:
|
||||
m.def("local_value", [](LocalType &l) { return l.i; });
|
||||
@ -70,9 +76,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
// test_nonlocal_failure
|
||||
// This registration will fail (global registration when LocalFail is already registered
|
||||
// globally in the main test module):
|
||||
m.def("register_nonlocal", [m]() {
|
||||
bind_local<NonLocalType, 0>(m, "NonLocalType");
|
||||
});
|
||||
m.def("register_nonlocal", [m]() { bind_local<NonLocalType, 0>(m, "NonLocalType"); });
|
||||
|
||||
// test_stl_bind_local
|
||||
// stl_bind.h binders defaults to py::module_local if the types are local or converting:
|
||||
@ -82,27 +86,21 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
// test_stl_bind_global
|
||||
// and global if the type (or one of the types, for the map) is global (so these will fail,
|
||||
// assuming pybind11_tests is already loaded):
|
||||
m.def("register_nonlocal_vec", [m]() {
|
||||
py::bind_vector<NonLocalVec>(m, "NonLocalVec");
|
||||
});
|
||||
m.def("register_nonlocal_map", [m]() {
|
||||
py::bind_map<NonLocalMap>(m, "NonLocalMap");
|
||||
});
|
||||
m.def("register_nonlocal_vec", [m]() { py::bind_vector<NonLocalVec>(m, "NonLocalVec"); });
|
||||
m.def("register_nonlocal_map", [m]() { py::bind_map<NonLocalMap>(m, "NonLocalMap"); });
|
||||
// The default can, however, be overridden to global using `py::module_local()` or
|
||||
// `py::module_local(false)`.
|
||||
// Explicitly made local:
|
||||
py::bind_vector<NonLocalVec2>(m, "NonLocalVec2", py::module_local());
|
||||
// Explicitly made global (and so will fail to bind):
|
||||
m.def("register_nonlocal_map2", [m]() {
|
||||
py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false));
|
||||
});
|
||||
m.def("register_nonlocal_map2",
|
||||
[m]() { py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false)); });
|
||||
|
||||
// test_mixed_local_global
|
||||
// We try this both with the global type registered first and vice versa (the order shouldn't
|
||||
// matter).
|
||||
m.def("register_mixed_global_local", [m]() {
|
||||
bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local());
|
||||
});
|
||||
m.def("register_mixed_global_local",
|
||||
[m]() { bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local()); });
|
||||
m.def("register_mixed_local_global", [m]() {
|
||||
bind_local<MixedLocalGlobal, 2000>(m, "MixedLocalGlobal", py::module_local(false));
|
||||
});
|
||||
@ -110,14 +108,14 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); });
|
||||
|
||||
// test_internal_locals_differ
|
||||
m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; });
|
||||
m.def("local_cpp_types_addr",
|
||||
[]() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; });
|
||||
|
||||
// test_stl_caster_vs_stl_bind
|
||||
py::bind_vector<std::vector<int>>(m, "VectorInt");
|
||||
|
||||
m.def("load_vector_via_binding", [](std::vector<int> &v) {
|
||||
return std::accumulate(v.begin(), v.end(), 0);
|
||||
});
|
||||
m.def("load_vector_via_binding",
|
||||
[](std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); });
|
||||
|
||||
// test_cross_module_calls
|
||||
m.def("return_self", [](LocalVec *v) { return v; });
|
||||
@ -127,11 +125,9 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
|
||||
public:
|
||||
explicit Dog(std::string name) : Pet(std::move(name)) {}
|
||||
};
|
||||
py::class_<pets::Pet>(m, "Pet", py::module_local())
|
||||
.def("name", &pets::Pet::name);
|
||||
py::class_<pets::Pet>(m, "Pet", py::module_local()).def("name", &pets::Pet::name);
|
||||
// Binding for local extending class:
|
||||
py::class_<Dog, pets::Pet>(m, "Dog")
|
||||
.def(py::init<std::string>());
|
||||
py::class_<Dog, pets::Pet>(m, "Dog").def(py::init<std::string>());
|
||||
m.def("pet_name", [](pets::Pet &p) { return p.name(); });
|
||||
|
||||
py::class_<MixGL>(m, "MixGL", py::module_local()).def(py::init<int>());
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include "constructor_stats.h"
|
||||
|
||||
#include <functional>
|
||||
@ -31,9 +32,7 @@ std::list<std::function<void(py::module_ &)>> &initializers() {
|
||||
return inits;
|
||||
}
|
||||
|
||||
test_initializer::test_initializer(Initializer init) {
|
||||
initializers().emplace_back(init);
|
||||
}
|
||||
test_initializer::test_initializer(Initializer init) { initializers().emplace_back(init); }
|
||||
|
||||
test_initializer::test_initializer(const char *submodule_name, Initializer init) {
|
||||
initializers().emplace_back([=](py::module_ &parent) {
|
||||
@ -51,15 +50,16 @@ void bind_ConstructorStats(py::module_ &m) {
|
||||
.def_readwrite("move_assignments", &ConstructorStats::move_assignments)
|
||||
.def_readwrite("copy_constructions", &ConstructorStats::copy_constructions)
|
||||
.def_readwrite("move_constructions", &ConstructorStats::move_constructions)
|
||||
.def_static("get", (ConstructorStats &(*)(py::object)) &ConstructorStats::get, py::return_value_policy::reference_internal)
|
||||
.def_static("get",
|
||||
(ConstructorStats & (*) (py::object)) & ConstructorStats::get,
|
||||
py::return_value_policy::reference_internal)
|
||||
|
||||
// Not exactly ConstructorStats, but related: expose the internal pybind number of registered instances
|
||||
// to allow instance cleanup checks (invokes a GC first)
|
||||
// Not exactly ConstructorStats, but related: expose the internal pybind number of
|
||||
// registered instances to allow instance cleanup checks (invokes a GC first)
|
||||
.def_static("detail_reg_inst", []() {
|
||||
ConstructorStats::gc();
|
||||
return py::detail::get_internals().registered_instances.size();
|
||||
})
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(pybind11_tests, m) {
|
||||
@ -79,12 +79,12 @@ PYBIND11_MODULE(pybind11_tests, m) {
|
||||
.def("get_value", &UserType::value, "Get value using a method")
|
||||
.def("set_value", &UserType::set, "Set value using a method")
|
||||
.def_property("value", &UserType::value, &UserType::set, "Get/set value using a property")
|
||||
.def("__repr__", [](const UserType& u) { return "UserType({})"_s.format(u.value()); });
|
||||
.def("__repr__", [](const UserType &u) { return "UserType({})"_s.format(u.value()); });
|
||||
|
||||
py::class_<IncType, UserType>(m, "IncType")
|
||||
.def(py::init<>())
|
||||
.def(py::init<int>())
|
||||
.def("__repr__", [](const IncType& u) { return "IncType({})"_s.format(u.value()); });
|
||||
.def("__repr__", [](const IncType &u) { return "IncType({})"_s.format(u.value()); });
|
||||
|
||||
for (const auto &initializer : initializers()) {
|
||||
initializer(m);
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/eval.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
// We get some really long type names here which causes MSVC 2015 to emit warnings
|
||||
# pragma warning(disable : 4503) // NOLINT: warning C4503: decorated name length exceeded, name was truncated
|
||||
# pragma warning( \
|
||||
disable : 4503) // NOLINT: warning C4503: decorated name length exceeded, name was truncated
|
||||
#endif
|
||||
|
||||
namespace py = pybind11;
|
||||
@ -25,13 +26,13 @@ public:
|
||||
void test_submodule_##name(py::module_ &(variable))
|
||||
|
||||
/// Dummy type which is not exported anywhere -- something to trigger a conversion error
|
||||
struct UnregisteredType { };
|
||||
struct UnregisteredType {};
|
||||
|
||||
/// A user-defined type which is exported and can be used by any test
|
||||
class UserType {
|
||||
public:
|
||||
UserType() = default;
|
||||
explicit UserType(int i) : i(i) { }
|
||||
explicit UserType(int i) : i(i) {}
|
||||
|
||||
int value() const { return i; }
|
||||
void set(int set) { i = set; }
|
||||
@ -45,7 +46,7 @@ class IncType : public UserType {
|
||||
public:
|
||||
using UserType::UserType;
|
||||
IncType() = default;
|
||||
IncType(const IncType &other) : IncType(other.value() + 1) { }
|
||||
IncType(const IncType &other) : IncType(other.value() + 1) {}
|
||||
IncType(IncType &&) = delete;
|
||||
IncType &operator=(const IncType &) = delete;
|
||||
IncType &operator=(IncType &&) = delete;
|
||||
@ -57,16 +58,21 @@ union IntFloat {
|
||||
float f;
|
||||
};
|
||||
|
||||
/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast context.
|
||||
/// Used to test recursive casters (e.g. std::tuple, stl containers).
|
||||
/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast
|
||||
/// context. Used to test recursive casters (e.g. std::tuple, stl containers).
|
||||
struct RValueCaster {};
|
||||
PYBIND11_NAMESPACE_BEGIN(pybind11)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template<> class type_caster<RValueCaster> {
|
||||
template <>
|
||||
class type_caster<RValueCaster> {
|
||||
public:
|
||||
PYBIND11_TYPE_CASTER(RValueCaster, const_name("RValueCaster"));
|
||||
static handle cast(RValueCaster &&, return_value_policy, handle) { return py::str("rvalue").release(); }
|
||||
static handle cast(const RValueCaster &, return_value_policy, handle) { return py::str("lvalue").release(); }
|
||||
static handle cast(RValueCaster &&, return_value_policy, handle) {
|
||||
return py::str("rvalue").release();
|
||||
}
|
||||
static handle cast(const RValueCaster &, return_value_policy, handle) {
|
||||
return py::str("lvalue").release();
|
||||
}
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
@ -80,5 +86,6 @@ void ignoreOldStyleInitWarnings(F &&body) {
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore", message=message, category=FutureWarning)
|
||||
body()
|
||||
)", py::dict(py::arg("body") = py::cpp_function(body)));
|
||||
)",
|
||||
py::dict(py::arg("body") = py::cpp_function(body)));
|
||||
}
|
||||
|
||||
@ -11,12 +11,11 @@
|
||||
|
||||
TEST_SUBMODULE(async_module, m) {
|
||||
struct DoesNotSupportAsync {};
|
||||
py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync")
|
||||
.def(py::init<>());
|
||||
py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync").def(py::init<>());
|
||||
struct SupportsAsync {};
|
||||
py::class_<SupportsAsync>(m, "SupportsAsync")
|
||||
.def(py::init<>())
|
||||
.def("__await__", [](const SupportsAsync& self) -> py::object {
|
||||
.def("__await__", [](const SupportsAsync &self) -> py::object {
|
||||
static_cast<void>(self);
|
||||
py::object loop = py::module_::import("asyncio.events").attr("get_event_loop")();
|
||||
py::object f = loop.attr("create_future")();
|
||||
|
||||
@ -7,10 +7,11 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
TEST_SUBMODULE(buffers, m) {
|
||||
// test_from_python / test_to_python:
|
||||
class Matrix {
|
||||
@ -23,7 +24,8 @@ TEST_SUBMODULE(buffers, m) {
|
||||
}
|
||||
|
||||
Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
|
||||
print_copy_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
print_copy_created(this,
|
||||
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
m_data = new float[(size_t) (m_rows * m_cols)];
|
||||
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
|
||||
@ -37,7 +39,8 @@ TEST_SUBMODULE(buffers, m) {
|
||||
}
|
||||
|
||||
~Matrix() {
|
||||
print_destroyed(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
print_destroyed(this,
|
||||
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
@ -56,27 +59,33 @@ TEST_SUBMODULE(buffers, m) {
|
||||
}
|
||||
|
||||
Matrix &operator=(Matrix &&s) noexcept {
|
||||
print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
print_move_assigned(this,
|
||||
std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
|
||||
if (&s != this) {
|
||||
delete[] m_data;
|
||||
m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data;
|
||||
s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr;
|
||||
m_rows = s.m_rows;
|
||||
m_cols = s.m_cols;
|
||||
m_data = s.m_data;
|
||||
s.m_rows = 0;
|
||||
s.m_cols = 0;
|
||||
s.m_data = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
float operator()(py::ssize_t i, py::ssize_t j) const {
|
||||
return m_data[(size_t) (i*m_cols + j)];
|
||||
return m_data[(size_t) (i * m_cols + j)];
|
||||
}
|
||||
|
||||
float &operator()(py::ssize_t i, py::ssize_t j) {
|
||||
return m_data[(size_t) (i*m_cols + j)];
|
||||
return m_data[(size_t) (i * m_cols + j)];
|
||||
}
|
||||
|
||||
float *data() { return m_data; }
|
||||
|
||||
py::ssize_t rows() const { return m_rows; }
|
||||
py::ssize_t cols() const { return m_cols; }
|
||||
|
||||
private:
|
||||
py::ssize_t m_rows;
|
||||
py::ssize_t m_cols;
|
||||
@ -117,11 +126,10 @@ TEST_SUBMODULE(buffers, m) {
|
||||
/// Provide buffer access
|
||||
.def_buffer([](Matrix &m) -> py::buffer_info {
|
||||
return py::buffer_info(
|
||||
m.data(), /* Pointer to buffer */
|
||||
{ m.rows(), m.cols() }, /* Buffer dimensions */
|
||||
{ sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
|
||||
sizeof(float) }
|
||||
);
|
||||
m.data(), /* Pointer to buffer */
|
||||
{m.rows(), m.cols()}, /* Buffer dimensions */
|
||||
{sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
|
||||
sizeof(float)});
|
||||
});
|
||||
|
||||
// test_inherited_protocol
|
||||
@ -130,9 +138,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
explicit SquareMatrix(py::ssize_t n) : Matrix(n, n) {}
|
||||
};
|
||||
// Derived classes inherit the buffer protocol and the buffer access function
|
||||
py::class_<SquareMatrix, Matrix>(m, "SquareMatrix")
|
||||
.def(py::init<py::ssize_t>());
|
||||
|
||||
py::class_<SquareMatrix, Matrix>(m, "SquareMatrix").def(py::init<py::ssize_t>());
|
||||
|
||||
// test_pointer_to_member_fn
|
||||
// Tests that passing a pointer to member to the base class works in
|
||||
@ -141,8 +147,8 @@ TEST_SUBMODULE(buffers, m) {
|
||||
int32_t value = 0;
|
||||
|
||||
py::buffer_info get_buffer_info() {
|
||||
return py::buffer_info(&value, sizeof(value),
|
||||
py::format_descriptor<int32_t>::format(), 1);
|
||||
return py::buffer_info(
|
||||
&value, sizeof(value), py::format_descriptor<int32_t>::format(), 1);
|
||||
}
|
||||
};
|
||||
py::class_<Buffer>(m, "Buffer", py::buffer_protocol())
|
||||
@ -150,7 +156,6 @@ TEST_SUBMODULE(buffers, m) {
|
||||
.def_readwrite("value", &Buffer::value)
|
||||
.def_buffer(&Buffer::get_buffer_info);
|
||||
|
||||
|
||||
class ConstBuffer {
|
||||
std::unique_ptr<int32_t> value;
|
||||
|
||||
@ -159,8 +164,8 @@ TEST_SUBMODULE(buffers, m) {
|
||||
void set_value(int32_t v) { *value = v; }
|
||||
|
||||
py::buffer_info get_buffer_info() const {
|
||||
return py::buffer_info(value.get(), sizeof(*value),
|
||||
py::format_descriptor<int32_t>::format(), 1);
|
||||
return py::buffer_info(
|
||||
value.get(), sizeof(*value), py::format_descriptor<int32_t>::format(), 1);
|
||||
}
|
||||
|
||||
ConstBuffer() : value(new int32_t{0}) {}
|
||||
@ -170,7 +175,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
.def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value)
|
||||
.def_buffer(&ConstBuffer::get_buffer_info);
|
||||
|
||||
struct DerivedBuffer : public Buffer { };
|
||||
struct DerivedBuffer : public Buffer {};
|
||||
py::class_<DerivedBuffer>(m, "DerivedBuffer", py::buffer_protocol())
|
||||
.def(py::init<>())
|
||||
.def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
|
||||
@ -180,9 +185,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
const uint8_t value = 0;
|
||||
explicit BufferReadOnly(uint8_t value) : value(value) {}
|
||||
|
||||
py::buffer_info get_buffer_info() {
|
||||
return py::buffer_info(&value, 1);
|
||||
}
|
||||
py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1); }
|
||||
};
|
||||
py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol())
|
||||
.def(py::init<uint8_t>())
|
||||
@ -192,9 +195,7 @@ TEST_SUBMODULE(buffers, m) {
|
||||
uint8_t value = 0;
|
||||
bool readonly = false;
|
||||
|
||||
py::buffer_info get_buffer_info() {
|
||||
return py::buffer_info(&value, 1, readonly);
|
||||
}
|
||||
py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1, readonly); }
|
||||
};
|
||||
py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol())
|
||||
.def(py::init<>())
|
||||
@ -213,9 +214,11 @@ TEST_SUBMODULE(buffers, m) {
|
||||
.def_readonly("strides", &py::buffer_info::strides)
|
||||
.def_readonly("readonly", &py::buffer_info::readonly)
|
||||
.def("__repr__", [](py::handle self) {
|
||||
return py::str("itemsize={0.itemsize!r}, size={0.size!r}, format={0.format!r}, ndim={0.ndim!r}, shape={0.shape!r}, strides={0.strides!r}, readonly={0.readonly!r}").format(self);
|
||||
})
|
||||
;
|
||||
return py::str("itemsize={0.itemsize!r}, size={0.size!r}, format={0.format!r}, "
|
||||
"ndim={0.ndim!r}, shape={0.shape!r}, strides={0.strides!r}, "
|
||||
"readonly={0.readonly!r}")
|
||||
.format(self);
|
||||
});
|
||||
|
||||
m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); });
|
||||
}
|
||||
|
||||
@ -7,64 +7,67 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/complex.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
struct ConstRefCasted {
|
||||
int tag;
|
||||
int tag;
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(pybind11)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <>
|
||||
class type_caster<ConstRefCasted> {
|
||||
public:
|
||||
static constexpr auto name = const_name<ConstRefCasted>();
|
||||
public:
|
||||
static constexpr auto name = const_name<ConstRefCasted>();
|
||||
|
||||
// Input is unimportant, a new value will always be constructed based on the
|
||||
// cast operator.
|
||||
bool load(handle, bool) { return true; }
|
||||
// Input is unimportant, a new value will always be constructed based on the
|
||||
// cast operator.
|
||||
bool load(handle, bool) { return true; }
|
||||
|
||||
explicit operator ConstRefCasted &&() {
|
||||
value = {1};
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
return std::move(value);
|
||||
}
|
||||
explicit operator ConstRefCasted &() {
|
||||
value = {2};
|
||||
return value;
|
||||
}
|
||||
explicit operator ConstRefCasted *() {
|
||||
value = {3};
|
||||
return &value;
|
||||
}
|
||||
explicit operator ConstRefCasted &&() {
|
||||
value = {1};
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
return std::move(value);
|
||||
}
|
||||
explicit operator ConstRefCasted &() {
|
||||
value = {2};
|
||||
return value;
|
||||
}
|
||||
explicit operator ConstRefCasted *() {
|
||||
value = {3};
|
||||
return &value;
|
||||
}
|
||||
|
||||
explicit operator const ConstRefCasted &() {
|
||||
value = {4};
|
||||
return value;
|
||||
}
|
||||
explicit operator const ConstRefCasted *() {
|
||||
value = {5};
|
||||
return &value;
|
||||
}
|
||||
explicit operator const ConstRefCasted &() {
|
||||
value = {4};
|
||||
return value;
|
||||
}
|
||||
explicit operator const ConstRefCasted *() {
|
||||
value = {5};
|
||||
return &value;
|
||||
}
|
||||
|
||||
// custom cast_op to explicitly propagate types to the conversion operators.
|
||||
template <typename T_>
|
||||
using cast_op_type =
|
||||
/// const
|
||||
conditional_t<
|
||||
std::is_same<remove_reference_t<T_>, const ConstRefCasted*>::value, const ConstRefCasted*,
|
||||
conditional_t<
|
||||
std::is_same<T_, const ConstRefCasted&>::value, const ConstRefCasted&,
|
||||
/// non-const
|
||||
conditional_t<
|
||||
std::is_same<remove_reference_t<T_>, ConstRefCasted*>::value, ConstRefCasted*,
|
||||
conditional_t<
|
||||
std::is_same<T_, ConstRefCasted&>::value, ConstRefCasted&,
|
||||
/* else */ConstRefCasted&&>>>>;
|
||||
// custom cast_op to explicitly propagate types to the conversion operators.
|
||||
template <typename T_>
|
||||
using cast_op_type =
|
||||
/// const
|
||||
conditional_t<
|
||||
std::is_same<remove_reference_t<T_>, const ConstRefCasted *>::value,
|
||||
const ConstRefCasted *,
|
||||
conditional_t<
|
||||
std::is_same<T_, const ConstRefCasted &>::value,
|
||||
const ConstRefCasted &,
|
||||
/// non-const
|
||||
conditional_t<std::is_same<remove_reference_t<T_>, ConstRefCasted *>::value,
|
||||
ConstRefCasted *,
|
||||
conditional_t<std::is_same<T_, ConstRefCasted &>::value,
|
||||
ConstRefCasted &,
|
||||
/* else */ ConstRefCasted &&>>>>;
|
||||
|
||||
private:
|
||||
ConstRefCasted value = {0};
|
||||
private:
|
||||
ConstRefCasted value = {0};
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
@ -74,25 +77,43 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
m.def("string_roundtrip", [](const char *s) { return s; });
|
||||
|
||||
// test_unicode_conversion
|
||||
// Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte
|
||||
char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, mathbfA32 = 0x1d400 /*𝐀*/;
|
||||
char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00;
|
||||
// Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null
|
||||
// byte
|
||||
char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/,
|
||||
mathbfA32 = 0x1d400 /*𝐀*/;
|
||||
char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82,
|
||||
mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00;
|
||||
std::wstring wstr;
|
||||
wstr.push_back(0x61); // a
|
||||
wstr.push_back(0x61); // a
|
||||
wstr.push_back(0x2e18); // ⸘
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16
|
||||
else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
|
||||
wstr.push_back(mathbfA16_1);
|
||||
wstr.push_back(mathbfA16_2);
|
||||
} // 𝐀, utf16
|
||||
else {
|
||||
wstr.push_back((wchar_t) mathbfA32);
|
||||
} // 𝐀, utf32
|
||||
wstr.push_back(0x7a); // z
|
||||
|
||||
m.def("good_utf8_string", []() { return std::string((const char*)u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀
|
||||
m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // b‽🎂𝐀z
|
||||
m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // a𝐀🎂‽z
|
||||
m.def("good_utf8_string", []() {
|
||||
return std::string((const char *) u8"Say utf8\u203d \U0001f382 \U0001d400");
|
||||
}); // Say utf8‽ 🎂 𝐀
|
||||
m.def("good_utf16_string", [=]() {
|
||||
return std::u16string({b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16});
|
||||
}); // b‽🎂𝐀z
|
||||
m.def("good_utf32_string", [=]() {
|
||||
return std::u32string({a32, mathbfA32, cake32, ib32, z32});
|
||||
}); // a𝐀🎂‽z
|
||||
m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z
|
||||
m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); });
|
||||
m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); });
|
||||
m.def("bad_utf8_string", []() {
|
||||
return std::string("abc\xd0"
|
||||
"def");
|
||||
});
|
||||
m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); });
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError
|
||||
m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); });
|
||||
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger
|
||||
// UnicodeDecodeError
|
||||
m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); });
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
|
||||
m.def("bad_wchar_string", [=]() {
|
||||
return std::wstring({wchar_t(0x61), wchar_t(0xd800)});
|
||||
@ -120,8 +141,13 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
|
||||
#ifdef PYBIND11_HAS_U8STRING
|
||||
m.attr("has_u8string") = true;
|
||||
m.def("good_utf8_u8string", []() { return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀
|
||||
m.def("bad_utf8_u8string", []() { return std::u8string((const char8_t*)"abc\xd0" "def"); });
|
||||
m.def("good_utf8_u8string", []() {
|
||||
return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400");
|
||||
}); // Say utf8‽ 🎂 𝐀
|
||||
m.def("bad_utf8_u8string", []() {
|
||||
return std::u8string((const char8_t *) "abc\xd0"
|
||||
"def");
|
||||
});
|
||||
|
||||
m.def("u8_char8_Z", []() -> char8_t { return u8'Z'; });
|
||||
|
||||
@ -133,46 +159,63 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
// test_string_view
|
||||
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||
m.attr("has_string_view") = true;
|
||||
m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view_chars", [](std::string_view s) { py::list l;
|
||||
m.def("string_view_chars", [](std::string_view s) {
|
||||
py::list l;
|
||||
for (auto c : s) {
|
||||
l.append((std::uint8_t) c);
|
||||
}
|
||||
return l; });
|
||||
m.def("string_view16_chars", [](std::u16string_view s) { py::list l;
|
||||
return l;
|
||||
});
|
||||
m.def("string_view16_chars", [](std::u16string_view s) {
|
||||
py::list l;
|
||||
for (auto c : s) {
|
||||
l.append((int) c);
|
||||
}
|
||||
return l; });
|
||||
m.def("string_view32_chars", [](std::u32string_view s) { py::list l;
|
||||
return l;
|
||||
});
|
||||
m.def("string_view32_chars", [](std::u32string_view s) {
|
||||
py::list l;
|
||||
for (auto c : s) {
|
||||
l.append((int) c);
|
||||
}
|
||||
return l; });
|
||||
m.def("string_view_return", []() { return std::string_view((const char*)u8"utf8 secret \U0001f382"); });
|
||||
m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); });
|
||||
m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); });
|
||||
return l;
|
||||
});
|
||||
m.def("string_view_return",
|
||||
[]() { return std::string_view((const char *) u8"utf8 secret \U0001f382"); });
|
||||
m.def("string_view16_return",
|
||||
[]() { return std::u16string_view(u"utf16 secret \U0001f382"); });
|
||||
m.def("string_view32_return",
|
||||
[]() { return std::u32string_view(U"utf32 secret \U0001f382"); });
|
||||
|
||||
// The inner lambdas here are to also test implicit conversion
|
||||
using namespace std::literals;
|
||||
m.def("string_view_bytes", []() { return [](py::bytes b) { return b; }("abc \x80\x80 def"sv); });
|
||||
m.def("string_view_str", []() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); });
|
||||
m.def("string_view_from_bytes", [](const py::bytes &b) { return [](std::string_view s) { return s; }(b); });
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
m.def("string_view_bytes",
|
||||
[]() { return [](py::bytes b) { return b; }("abc \x80\x80 def"sv); });
|
||||
m.def("string_view_str",
|
||||
[]() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); });
|
||||
m.def("string_view_from_bytes",
|
||||
[](const py::bytes &b) { return [](std::string_view s) { return s; }(b); });
|
||||
# if PY_MAJOR_VERSION >= 3
|
||||
m.def("string_view_memoryview", []() {
|
||||
static constexpr auto val = "Have some \360\237\216\202"sv;
|
||||
return py::memoryview::from_memory(val);
|
||||
});
|
||||
#endif
|
||||
# endif
|
||||
|
||||
# ifdef PYBIND11_HAS_U8STRING
|
||||
m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view8_chars", [](std::u8string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; });
|
||||
# ifdef PYBIND11_HAS_U8STRING
|
||||
m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); });
|
||||
m.def("string_view8_chars", [](std::u8string_view s) {
|
||||
py::list l;
|
||||
for (auto c : s)
|
||||
l.append((std::uint8_t) c);
|
||||
return l;
|
||||
});
|
||||
m.def("string_view8_return", []() { return std::u8string_view(u8"utf8 secret \U0001f382"); });
|
||||
m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; });
|
||||
# endif
|
||||
m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; });
|
||||
# endif
|
||||
|
||||
struct TypeWithBothOperatorStringAndStringView {
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
@ -194,7 +237,8 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
|
||||
// test_int_convert
|
||||
m.def("int_passthrough", [](int arg) { return arg; });
|
||||
m.def("int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert());
|
||||
m.def(
|
||||
"int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert());
|
||||
|
||||
// test_tuple
|
||||
m.def(
|
||||
@ -203,19 +247,27 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
return std::make_pair(input.second, input.first);
|
||||
},
|
||||
"Return a pair in reversed order");
|
||||
m.def("tuple_passthrough", [](std::tuple<bool, std::string, int> input) {
|
||||
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
|
||||
}, "Return a triple in reversed order");
|
||||
m.def(
|
||||
"tuple_passthrough",
|
||||
[](std::tuple<bool, std::string, int> input) {
|
||||
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
|
||||
},
|
||||
"Return a triple in reversed order");
|
||||
m.def("empty_tuple", []() { return std::tuple<>(); });
|
||||
static std::pair<RValueCaster, RValueCaster> lvpair;
|
||||
static std::tuple<RValueCaster, RValueCaster, RValueCaster> lvtuple;
|
||||
static std::pair<RValueCaster, std::tuple<RValueCaster, std::pair<RValueCaster, RValueCaster>>> lvnested;
|
||||
static std::pair<RValueCaster, std::tuple<RValueCaster, std::pair<RValueCaster, RValueCaster>>>
|
||||
lvnested;
|
||||
m.def("rvalue_pair", []() { return std::make_pair(RValueCaster{}, RValueCaster{}); });
|
||||
m.def("lvalue_pair", []() -> const decltype(lvpair) & { return lvpair; });
|
||||
m.def("rvalue_tuple", []() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); });
|
||||
m.def("rvalue_tuple",
|
||||
[]() { return std::make_tuple(RValueCaster{}, RValueCaster{}, RValueCaster{}); });
|
||||
m.def("lvalue_tuple", []() -> const decltype(lvtuple) & { return lvtuple; });
|
||||
m.def("rvalue_nested", []() {
|
||||
return std::make_pair(RValueCaster{}, std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{}))); });
|
||||
return std::make_pair(
|
||||
RValueCaster{},
|
||||
std::make_tuple(RValueCaster{}, std::make_pair(RValueCaster{}, RValueCaster{})));
|
||||
});
|
||||
m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; });
|
||||
|
||||
static std::pair<int, std::string> int_string_pair{2, "items"};
|
||||
@ -223,11 +275,11 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
|
||||
// test_builtins_cast_return_none
|
||||
m.def("return_none_string", []() -> std::string * { return nullptr; });
|
||||
m.def("return_none_char", []() -> const char * { return nullptr; });
|
||||
m.def("return_none_bool", []() -> bool * { return nullptr; });
|
||||
m.def("return_none_int", []() -> int * { return nullptr; });
|
||||
m.def("return_none_float", []() -> float * { return nullptr; });
|
||||
m.def("return_none_pair", []() -> std::pair<int,int> * { return nullptr; });
|
||||
m.def("return_none_char", []() -> const char * { return nullptr; });
|
||||
m.def("return_none_bool", []() -> bool * { return nullptr; });
|
||||
m.def("return_none_int", []() -> int * { return nullptr; });
|
||||
m.def("return_none_float", []() -> float * { return nullptr; });
|
||||
m.def("return_none_pair", []() -> std::pair<int, int> * { return nullptr; });
|
||||
|
||||
// test_none_deferred
|
||||
m.def("defer_none_cstring", [](char *) { return false; });
|
||||
@ -245,7 +297,8 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
|
||||
// test_bool_caster
|
||||
m.def("bool_passthrough", [](bool arg) { return arg; });
|
||||
m.def("bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert());
|
||||
m.def(
|
||||
"bool_passthrough_noconvert", [](bool arg) { return arg; }, py::arg{}.noconvert());
|
||||
|
||||
// TODO: This should be disabled and fixed in future Intel compilers
|
||||
#if !defined(__INTEL_COMPILER)
|
||||
@ -253,13 +306,15 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
// When compiled with the Intel compiler, this results in segmentation faults when importing
|
||||
// the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when
|
||||
// a newer version of icc is available.
|
||||
m.def("bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert());
|
||||
m.def(
|
||||
"bool_passthrough_noconvert2", [](bool arg) { return arg; }, py::arg().noconvert());
|
||||
#endif
|
||||
|
||||
// test_reference_wrapper
|
||||
m.def("refwrap_builtin", [](std::reference_wrapper<int> p) { return 10 * p.get(); });
|
||||
m.def("refwrap_usertype", [](std::reference_wrapper<UserType> p) { return p.get().value(); });
|
||||
m.def("refwrap_usertype_const", [](std::reference_wrapper<const UserType> p) { return p.get().value(); });
|
||||
m.def("refwrap_usertype_const",
|
||||
[](std::reference_wrapper<const UserType> p) { return p.get().value(); });
|
||||
|
||||
m.def("refwrap_lvalue", []() -> std::reference_wrapper<UserType> {
|
||||
static UserType x(1);
|
||||
@ -272,17 +327,20 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
|
||||
// Not currently supported (std::pair caster has return-by-value cast operator);
|
||||
// triggers static_assert failure.
|
||||
//m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
|
||||
// m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
|
||||
|
||||
m.def("refwrap_list", [](bool copy) {
|
||||
static IncType x1(1), x2(2);
|
||||
py::list l;
|
||||
for (const auto &f : {std::ref(x1), std::ref(x2)}) {
|
||||
l.append(py::cast(f, copy ? py::return_value_policy::copy
|
||||
: py::return_value_policy::reference));
|
||||
}
|
||||
return l;
|
||||
}, "copy"_a);
|
||||
m.def(
|
||||
"refwrap_list",
|
||||
[](bool copy) {
|
||||
static IncType x1(1), x2(2);
|
||||
py::list l;
|
||||
for (const auto &f : {std::ref(x1), std::ref(x2)}) {
|
||||
l.append(py::cast(
|
||||
f, copy ? py::return_value_policy::copy : py::return_value_policy::reference));
|
||||
}
|
||||
return l;
|
||||
},
|
||||
"copy"_a);
|
||||
|
||||
m.def("refwrap_iiw", [](const IncType &w) { return w.value(); });
|
||||
m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) {
|
||||
@ -299,12 +357,13 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
|
||||
// test_complex
|
||||
m.def("complex_cast", [](float x) { return "{}"_s.format(x); });
|
||||
m.def("complex_cast", [](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); });
|
||||
m.def("complex_cast",
|
||||
[](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); });
|
||||
|
||||
// test int vs. long (Python 2)
|
||||
m.def("int_cast", []() {return (int) 42;});
|
||||
m.def("long_cast", []() {return (long) 42;});
|
||||
m.def("longlong_cast", []() {return ULLONG_MAX;});
|
||||
m.def("int_cast", []() { return (int) 42; });
|
||||
m.def("long_cast", []() { return (long) 42; });
|
||||
m.def("longlong_cast", []() { return ULLONG_MAX; });
|
||||
|
||||
/// test void* cast operator
|
||||
m.def("test_void_caster", []() -> bool {
|
||||
@ -315,11 +374,12 @@ TEST_SUBMODULE(builtin_casters, m) {
|
||||
|
||||
// Tests const/non-const propagation in cast_op.
|
||||
m.def("takes", [](ConstRefCasted x) { return x.tag; });
|
||||
m.def("takes_move", [](ConstRefCasted&& x) { return x.tag; });
|
||||
m.def("takes_ptr", [](ConstRefCasted* x) { return x->tag; });
|
||||
m.def("takes_ref", [](ConstRefCasted& x) { return x.tag; });
|
||||
m.def("takes_move", [](ConstRefCasted &&x) { return x.tag; });
|
||||
m.def("takes_ptr", [](ConstRefCasted *x) { return x->tag; });
|
||||
m.def("takes_ref", [](ConstRefCasted &x) { return x.tag; });
|
||||
m.def("takes_ref_wrap", [](std::reference_wrapper<ConstRefCasted> x) { return x.get().tag; });
|
||||
m.def("takes_const_ptr", [](const ConstRefCasted* x) { return x->tag; });
|
||||
m.def("takes_const_ref", [](const ConstRefCasted& x) { return x.tag; });
|
||||
m.def("takes_const_ref_wrap", [](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; });
|
||||
m.def("takes_const_ptr", [](const ConstRefCasted *x) { return x->tag; });
|
||||
m.def("takes_const_ref", [](const ConstRefCasted &x) { return x.tag; });
|
||||
m.def("takes_const_ref_wrap",
|
||||
[](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; });
|
||||
}
|
||||
|
||||
@ -40,18 +40,17 @@ TEST_SUBMODULE(call_policies, m) {
|
||||
Child(Child &&) = default;
|
||||
~Child() { py::print("Releasing child."); }
|
||||
};
|
||||
py::class_<Child>(m, "Child")
|
||||
.def(py::init<>());
|
||||
py::class_<Child>(m, "Child").def(py::init<>());
|
||||
|
||||
class Parent {
|
||||
public:
|
||||
Parent() { py::print("Allocating parent."); }
|
||||
Parent(const Parent& parent) = default;
|
||||
Parent(const Parent &parent) = default;
|
||||
~Parent() { py::print("Releasing parent."); }
|
||||
void addChild(Child *) { }
|
||||
void addChild(Child *) {}
|
||||
Child *returnChild() { return new Child(); }
|
||||
Child *returnNullChild() { return nullptr; }
|
||||
static Child *staticFunction(Parent*) { return new Child(); }
|
||||
static Child *staticFunction(Parent *) { return new Child(); }
|
||||
};
|
||||
py::class_<Parent>(m, "Parent")
|
||||
.def(py::init<>())
|
||||
@ -62,11 +61,12 @@ TEST_SUBMODULE(call_policies, m) {
|
||||
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
|
||||
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
|
||||
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>())
|
||||
.def_static(
|
||||
"staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
|
||||
.def_static("staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
|
||||
|
||||
m.def("free_function", [](Parent*, Child*) {}, py::keep_alive<1, 2>());
|
||||
m.def("invalid_arg_index", []{}, py::keep_alive<0, 1>());
|
||||
m.def(
|
||||
"free_function", [](Parent *, Child *) {}, py::keep_alive<1, 2>());
|
||||
m.def(
|
||||
"invalid_arg_index", [] {}, py::keep_alive<0, 1>());
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
// test_alive_gc
|
||||
@ -74,21 +74,28 @@ TEST_SUBMODULE(call_policies, m) {
|
||||
public:
|
||||
using Parent::Parent;
|
||||
};
|
||||
py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr())
|
||||
.def(py::init<>());
|
||||
py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()).def(py::init<>());
|
||||
#endif
|
||||
|
||||
// test_call_guard
|
||||
m.def("unguarded_call", &CustomGuard::report_status);
|
||||
m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>());
|
||||
|
||||
m.def("multiple_guards_correct_order", []() {
|
||||
return CustomGuard::report_status() + std::string(" & ") + DependentGuard::report_status();
|
||||
}, py::call_guard<CustomGuard, DependentGuard>());
|
||||
m.def(
|
||||
"multiple_guards_correct_order",
|
||||
[]() {
|
||||
return CustomGuard::report_status() + std::string(" & ")
|
||||
+ DependentGuard::report_status();
|
||||
},
|
||||
py::call_guard<CustomGuard, DependentGuard>());
|
||||
|
||||
m.def("multiple_guards_wrong_order", []() {
|
||||
return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status();
|
||||
}, py::call_guard<DependentGuard, CustomGuard>());
|
||||
m.def(
|
||||
"multiple_guards_wrong_order",
|
||||
[]() {
|
||||
return DependentGuard::report_status() + std::string(" & ")
|
||||
+ CustomGuard::report_status();
|
||||
},
|
||||
py::call_guard<DependentGuard, CustomGuard>());
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
|
||||
// `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
|
||||
|
||||
@ -7,11 +7,12 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/functional.h>
|
||||
#include <thread>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
int dummy_function(int i) { return i + 1; }
|
||||
|
||||
@ -20,11 +21,12 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
m.def("test_callback1", [](const py::object &func) { return func(); });
|
||||
m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); });
|
||||
m.def("test_callback3", [](const std::function<int(int)> &func) {
|
||||
return "func(43) = " + std::to_string(func(43)); });
|
||||
m.def("test_callback4", []() -> std::function<int(int)> { return [](int i) { return i+1; }; });
|
||||
m.def("test_callback5", []() {
|
||||
return py::cpp_function([](int i) { return i+1; }, py::arg("number"));
|
||||
return "func(43) = " + std::to_string(func(43));
|
||||
});
|
||||
m.def("test_callback4",
|
||||
[]() -> std::function<int(int)> { return [](int i) { return i + 1; }; });
|
||||
m.def("test_callback5",
|
||||
[]() { return py::cpp_function([](int i) { return i + 1; }, py::arg("number")); });
|
||||
|
||||
// test_keyword_args_and_generalized_unpacking
|
||||
m.def("test_tuple_unpacking", [](const py::function &f) {
|
||||
@ -34,9 +36,9 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
});
|
||||
|
||||
m.def("test_dict_unpacking", [](const py::function &f) {
|
||||
auto d1 = py::dict("key"_a="value", "a"_a=1);
|
||||
auto d1 = py::dict("key"_a = "value", "a"_a = 1);
|
||||
auto d2 = py::dict();
|
||||
auto d3 = py::dict("b"_a=2);
|
||||
auto d3 = py::dict("b"_a = 2);
|
||||
return f("positional", 1, **d1, **d2, **d3);
|
||||
});
|
||||
|
||||
@ -44,32 +46,40 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
|
||||
m.def("test_unpacking_and_keywords1", [](const py::function &f) {
|
||||
auto args = py::make_tuple(2);
|
||||
auto kwargs = py::dict("d"_a=4);
|
||||
return f(1, *args, "c"_a=3, **kwargs);
|
||||
auto kwargs = py::dict("d"_a = 4);
|
||||
return f(1, *args, "c"_a = 3, **kwargs);
|
||||
});
|
||||
|
||||
m.def("test_unpacking_and_keywords2", [](const py::function &f) {
|
||||
auto kwargs1 = py::dict("a"_a=1);
|
||||
auto kwargs2 = py::dict("c"_a=3, "d"_a=4);
|
||||
return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5,
|
||||
"key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5);
|
||||
auto kwargs1 = py::dict("a"_a = 1);
|
||||
auto kwargs2 = py::dict("c"_a = 3, "d"_a = 4);
|
||||
return f("positional",
|
||||
*py::make_tuple(1),
|
||||
2,
|
||||
*py::make_tuple(3, 4),
|
||||
5,
|
||||
"key"_a = "value",
|
||||
**kwargs1,
|
||||
"b"_a = 2,
|
||||
**kwargs2,
|
||||
"e"_a = 5);
|
||||
});
|
||||
|
||||
m.def("test_unpacking_error1", [](const py::function &f) {
|
||||
auto kwargs = py::dict("x"_a=3);
|
||||
return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword
|
||||
auto kwargs = py::dict("x"_a = 3);
|
||||
return f("x"_a = 1, "y"_a = 2, **kwargs); // duplicate ** after keyword
|
||||
});
|
||||
|
||||
m.def("test_unpacking_error2", [](const py::function &f) {
|
||||
auto kwargs = py::dict("x"_a=3);
|
||||
return f(**kwargs, "x"_a=1); // duplicate keyword after **
|
||||
auto kwargs = py::dict("x"_a = 3);
|
||||
return f(**kwargs, "x"_a = 1); // duplicate keyword after **
|
||||
});
|
||||
|
||||
m.def("test_arg_conversion_error1",
|
||||
[](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); });
|
||||
|
||||
m.def("test_arg_conversion_error2", [](const py::function &f) {
|
||||
f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567);
|
||||
f(234, "expected_name"_a = UnregisteredType(), "kw"_a = 567);
|
||||
});
|
||||
|
||||
// test_lambda_closure_cleanup
|
||||
@ -158,7 +168,6 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
return "matches dummy_function: eval(1) = " + std::to_string(r);
|
||||
}
|
||||
return "argument does NOT match dummy_function. This should never happen!";
|
||||
|
||||
});
|
||||
|
||||
class AbstractBase {
|
||||
@ -190,7 +199,7 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
// test_movable_object
|
||||
m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) {
|
||||
auto x = MovableObject();
|
||||
f(x); // lvalue reference shouldn't move out object
|
||||
f(x); // lvalue reference shouldn't move out object
|
||||
return x.valid; // must still return `true`
|
||||
});
|
||||
|
||||
@ -202,9 +211,10 @@ TEST_SUBMODULE(callbacks, m) {
|
||||
|
||||
// This checks that builtin functions can be passed as callbacks
|
||||
// rather than throwing RuntimeError due to trying to extract as capsule
|
||||
m.def("test_sum_builtin", [](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) {
|
||||
return sum_builtin(i);
|
||||
});
|
||||
m.def("test_sum_builtin",
|
||||
[](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) {
|
||||
return sum_builtin(i);
|
||||
});
|
||||
|
||||
// test async Python callbacks
|
||||
using callback_f = std::function<void(int)>;
|
||||
|
||||
@ -8,21 +8,20 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/chrono.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
struct different_resolutions {
|
||||
using time_point_h = std::chrono::time_point<
|
||||
std::chrono::system_clock, std::chrono::hours>;
|
||||
using time_point_m = std::chrono::time_point<
|
||||
std::chrono::system_clock, std::chrono::minutes>;
|
||||
using time_point_s = std::chrono::time_point<
|
||||
std::chrono::system_clock, std::chrono::seconds>;
|
||||
using time_point_ms = std::chrono::time_point<
|
||||
std::chrono::system_clock, std::chrono::milliseconds>;
|
||||
using time_point_us = std::chrono::time_point<
|
||||
std::chrono::system_clock, std::chrono::microseconds>;
|
||||
using time_point_h = std::chrono::time_point<std::chrono::system_clock, std::chrono::hours>;
|
||||
using time_point_m = std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>;
|
||||
using time_point_s = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
|
||||
using time_point_ms
|
||||
= std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>;
|
||||
using time_point_us
|
||||
= std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds>;
|
||||
time_point_h timestamp_h;
|
||||
time_point_m timestamp_m;
|
||||
time_point_s timestamp_s;
|
||||
@ -65,12 +64,11 @@ TEST_SUBMODULE(chrono, m) {
|
||||
// Roundtrip a duration in microseconds from a float argument
|
||||
m.def("test_chrono7", [](std::chrono::microseconds t) { return t; });
|
||||
// Float durations (issue #719)
|
||||
m.def("test_chrono_float_diff", [](std::chrono::duration<float> a, std::chrono::duration<float> b) {
|
||||
return a - b; });
|
||||
m.def("test_chrono_float_diff",
|
||||
[](std::chrono::duration<float> a, std::chrono::duration<float> b) { return a - b; });
|
||||
|
||||
m.def("test_nano_timepoint", [](timestamp start, timespan delta) -> timestamp {
|
||||
return start + delta;
|
||||
});
|
||||
m.def("test_nano_timepoint",
|
||||
[](timestamp start, timespan delta) -> timestamp { return start + delta; });
|
||||
|
||||
// Test different resolutions
|
||||
py::class_<different_resolutions>(m, "different_resolutions")
|
||||
@ -79,6 +77,5 @@ TEST_SUBMODULE(chrono, m) {
|
||||
.def_readwrite("timestamp_m", &different_resolutions::timestamp_m)
|
||||
.def_readwrite("timestamp_s", &different_resolutions::timestamp_s)
|
||||
.def_readwrite("timestamp_ms", &different_resolutions::timestamp_ms)
|
||||
.def_readwrite("timestamp_us", &different_resolutions::timestamp_us)
|
||||
;
|
||||
.def_readwrite("timestamp_us", &different_resolutions::timestamp_us);
|
||||
}
|
||||
|
||||
@ -11,18 +11,19 @@
|
||||
// Intel compiler requires a separate header file to support aligned new operators
|
||||
// and does not set the __cpp_aligned_new feature macro.
|
||||
// This header needs to be included before pybind11.
|
||||
#include <aligned_new>
|
||||
# include <aligned_new>
|
||||
#endif
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "local_bindings.h"
|
||||
#include <pybind11/stl.h>
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable: 4324)
|
||||
# pragma warning(disable : 4324)
|
||||
// warning C4324: structure was padded due to alignment specifier
|
||||
#endif
|
||||
|
||||
@ -75,6 +76,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
: m_name(name), m_species(species) {}
|
||||
std::string name() const { return m_name; }
|
||||
std::string species() const { return m_species; }
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_species;
|
||||
@ -101,27 +103,24 @@ TEST_SUBMODULE(class_, m) {
|
||||
};
|
||||
|
||||
py::class_<Pet> pet_class(m, "Pet");
|
||||
pet_class
|
||||
.def(py::init<std::string, std::string>())
|
||||
pet_class.def(py::init<std::string, std::string>())
|
||||
.def("name", &Pet::name)
|
||||
.def("species", &Pet::species);
|
||||
|
||||
/* One way of declaring a subclass relationship: reference parent's class_ object */
|
||||
py::class_<Dog>(m, "Dog", pet_class)
|
||||
.def(py::init<std::string>());
|
||||
py::class_<Dog>(m, "Dog", pet_class).def(py::init<std::string>());
|
||||
|
||||
/* Another way of declaring a subclass relationship: reference parent's C++ type */
|
||||
py::class_<Rabbit, Pet>(m, "Rabbit")
|
||||
.def(py::init<std::string>());
|
||||
py::class_<Rabbit, Pet>(m, "Rabbit").def(py::init<std::string>());
|
||||
|
||||
/* And another: list parent in class template arguments */
|
||||
py::class_<Hamster, Pet>(m, "Hamster")
|
||||
.def(py::init<std::string>());
|
||||
py::class_<Hamster, Pet>(m, "Hamster").def(py::init<std::string>());
|
||||
|
||||
/* Constructors are not inherited by default */
|
||||
py::class_<Chimera, Pet>(m, "Chimera");
|
||||
|
||||
m.def("pet_name_species", [](const Pet &pet) { return pet.name() + " is a " + pet.species(); });
|
||||
m.def("pet_name_species",
|
||||
[](const Pet &pet) { return pet.name() + " is a " + pet.species(); });
|
||||
m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
|
||||
|
||||
// test_automatic_upcasting
|
||||
@ -131,15 +130,15 @@ TEST_SUBMODULE(class_, m) {
|
||||
BaseClass(BaseClass &&) = default;
|
||||
virtual ~BaseClass() = default;
|
||||
};
|
||||
struct DerivedClass1 : BaseClass { };
|
||||
struct DerivedClass2 : BaseClass { };
|
||||
struct DerivedClass1 : BaseClass {};
|
||||
struct DerivedClass2 : BaseClass {};
|
||||
|
||||
py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
|
||||
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
|
||||
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
|
||||
|
||||
m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); });
|
||||
m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); });
|
||||
m.def("return_class_1", []() -> BaseClass * { return new DerivedClass1(); });
|
||||
m.def("return_class_2", []() -> BaseClass * { return new DerivedClass2(); });
|
||||
m.def("return_class_n", [](int n) -> BaseClass * {
|
||||
if (n == 1) {
|
||||
return new DerivedClass1();
|
||||
@ -149,19 +148,17 @@ TEST_SUBMODULE(class_, m) {
|
||||
}
|
||||
return new BaseClass();
|
||||
});
|
||||
m.def("return_none", []() -> BaseClass* { return nullptr; });
|
||||
m.def("return_none", []() -> BaseClass * { return nullptr; });
|
||||
|
||||
// test_isinstance
|
||||
m.def("check_instances", [](const py::list &l) {
|
||||
return py::make_tuple(
|
||||
py::isinstance<py::tuple>(l[0]),
|
||||
py::isinstance<py::dict>(l[1]),
|
||||
py::isinstance<Pet>(l[2]),
|
||||
py::isinstance<Pet>(l[3]),
|
||||
py::isinstance<Dog>(l[4]),
|
||||
py::isinstance<Rabbit>(l[5]),
|
||||
py::isinstance<UnregisteredType>(l[6])
|
||||
);
|
||||
return py::make_tuple(py::isinstance<py::tuple>(l[0]),
|
||||
py::isinstance<py::dict>(l[1]),
|
||||
py::isinstance<Pet>(l[2]),
|
||||
py::isinstance<Pet>(l[3]),
|
||||
py::isinstance<Dog>(l[4]),
|
||||
py::isinstance<Rabbit>(l[5]),
|
||||
py::isinstance<UnregisteredType>(l[6]));
|
||||
});
|
||||
|
||||
struct Invalid {};
|
||||
@ -180,18 +177,16 @@ TEST_SUBMODULE(class_, m) {
|
||||
|
||||
m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); });
|
||||
|
||||
m.def("get_type_classic", [](py::handle h) {
|
||||
return h.get_type();
|
||||
});
|
||||
m.def("get_type_classic", [](py::handle h) { return h.get_type(); });
|
||||
|
||||
m.def("as_type", [](const py::object &ob) { return py::type(ob); });
|
||||
|
||||
// test_mismatched_holder
|
||||
struct MismatchBase1 { };
|
||||
struct MismatchDerived1 : MismatchBase1 { };
|
||||
struct MismatchBase1 {};
|
||||
struct MismatchDerived1 : MismatchBase1 {};
|
||||
|
||||
struct MismatchBase2 { };
|
||||
struct MismatchDerived2 : MismatchBase2 { };
|
||||
struct MismatchBase2 {};
|
||||
struct MismatchDerived2 : MismatchBase2 {};
|
||||
|
||||
m.def("mismatched_holder_1", []() {
|
||||
auto mod = py::module_::import("__main__");
|
||||
@ -201,16 +196,14 @@ TEST_SUBMODULE(class_, m) {
|
||||
m.def("mismatched_holder_2", []() {
|
||||
auto mod = py::module_::import("__main__");
|
||||
py::class_<MismatchBase2>(mod, "MismatchBase2");
|
||||
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>,
|
||||
MismatchBase2>(mod, "MismatchDerived2");
|
||||
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(
|
||||
mod, "MismatchDerived2");
|
||||
});
|
||||
|
||||
// test_override_static
|
||||
// #511: problem with inheritance + overwritten def_static
|
||||
struct MyBase {
|
||||
static std::unique_ptr<MyBase> make() {
|
||||
return std::unique_ptr<MyBase>(new MyBase());
|
||||
}
|
||||
static std::unique_ptr<MyBase> make() { return std::unique_ptr<MyBase>(new MyBase()); }
|
||||
};
|
||||
|
||||
struct MyDerived : MyBase {
|
||||
@ -219,8 +212,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
}
|
||||
};
|
||||
|
||||
py::class_<MyBase>(m, "MyBase")
|
||||
.def_static("make", &MyBase::make);
|
||||
py::class_<MyBase>(m, "MyBase").def_static("make", &MyBase::make);
|
||||
|
||||
py::class_<MyDerived, MyBase>(m, "MyDerived")
|
||||
.def_static("make", &MyDerived::make)
|
||||
@ -233,8 +225,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
explicit ConvertibleFromUserType(UserType u) : i(u.value()) {}
|
||||
};
|
||||
|
||||
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType")
|
||||
.def(py::init<UserType>());
|
||||
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType").def(py::init<UserType>());
|
||||
py::implicitly_convertible<UserType, ConvertibleFromUserType>();
|
||||
|
||||
m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; });
|
||||
@ -257,31 +248,60 @@ TEST_SUBMODULE(class_, m) {
|
||||
};
|
||||
|
||||
auto *def = new PyMethodDef{"f", f, METH_VARARGS, nullptr};
|
||||
py::capsule def_capsule(def, [](void *ptr) { delete reinterpret_cast<PyMethodDef *>(ptr); });
|
||||
return py::reinterpret_steal<py::object>(PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr()));
|
||||
py::capsule def_capsule(def,
|
||||
[](void *ptr) { delete reinterpret_cast<PyMethodDef *>(ptr); });
|
||||
return py::reinterpret_steal<py::object>(
|
||||
PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr()));
|
||||
}());
|
||||
|
||||
// test_operator_new_delete
|
||||
struct HasOpNewDel {
|
||||
std::uint64_t i;
|
||||
static void *operator new(size_t s) { py::print("A new", s); return ::operator new(s); }
|
||||
static void *operator new(size_t s, void *ptr) { py::print("A placement-new", s); return ptr; }
|
||||
static void operator delete(void *p) { py::print("A delete"); return ::operator delete(p); }
|
||||
static void *operator new(size_t s) {
|
||||
py::print("A new", s);
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t s, void *ptr) {
|
||||
py::print("A placement-new", s);
|
||||
return ptr;
|
||||
}
|
||||
static void operator delete(void *p) {
|
||||
py::print("A delete");
|
||||
return ::operator delete(p);
|
||||
}
|
||||
};
|
||||
struct HasOpNewDelSize {
|
||||
std::uint32_t i;
|
||||
static void *operator new(size_t s) { py::print("B new", s); return ::operator new(s); }
|
||||
static void *operator new(size_t s, void *ptr) { py::print("B placement-new", s); return ptr; }
|
||||
static void operator delete(void *p, size_t s) { py::print("B delete", s); return ::operator delete(p); }
|
||||
static void *operator new(size_t s) {
|
||||
py::print("B new", s);
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t s, void *ptr) {
|
||||
py::print("B placement-new", s);
|
||||
return ptr;
|
||||
}
|
||||
static void operator delete(void *p, size_t s) {
|
||||
py::print("B delete", s);
|
||||
return ::operator delete(p);
|
||||
}
|
||||
};
|
||||
struct AliasedHasOpNewDelSize {
|
||||
std::uint64_t i;
|
||||
static void *operator new(size_t s) { py::print("C new", s); return ::operator new(s); }
|
||||
static void *operator new(size_t s, void *ptr) { py::print("C placement-new", s); return ptr; }
|
||||
static void operator delete(void *p, size_t s) { py::print("C delete", s); return ::operator delete(p); }
|
||||
static void *operator new(size_t s) {
|
||||
py::print("C new", s);
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t s, void *ptr) {
|
||||
py::print("C placement-new", s);
|
||||
return ptr;
|
||||
}
|
||||
static void operator delete(void *p, size_t s) {
|
||||
py::print("C delete", s);
|
||||
return ::operator delete(p);
|
||||
}
|
||||
virtual ~AliasedHasOpNewDelSize() = default;
|
||||
AliasedHasOpNewDelSize() = default;
|
||||
AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize&) = delete;
|
||||
AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize &) = delete;
|
||||
};
|
||||
struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize {
|
||||
PyAliasedHasOpNewDelSize() = default;
|
||||
@ -290,15 +310,28 @@ TEST_SUBMODULE(class_, m) {
|
||||
};
|
||||
struct HasOpNewDelBoth {
|
||||
std::uint32_t i[8];
|
||||
static void *operator new(size_t s) { py::print("D new", s); return ::operator new(s); }
|
||||
static void *operator new(size_t s, void *ptr) { py::print("D placement-new", s); return ptr; }
|
||||
static void operator delete(void *p) { py::print("D delete"); return ::operator delete(p); }
|
||||
static void operator delete(void *p, size_t s) { py::print("D wrong delete", s); return ::operator delete(p); }
|
||||
static void *operator new(size_t s) {
|
||||
py::print("D new", s);
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t s, void *ptr) {
|
||||
py::print("D placement-new", s);
|
||||
return ptr;
|
||||
}
|
||||
static void operator delete(void *p) {
|
||||
py::print("D delete");
|
||||
return ::operator delete(p);
|
||||
}
|
||||
static void operator delete(void *p, size_t s) {
|
||||
py::print("D wrong delete", s);
|
||||
return ::operator delete(p);
|
||||
}
|
||||
};
|
||||
py::class_<HasOpNewDel>(m, "HasOpNewDel").def(py::init<>());
|
||||
py::class_<HasOpNewDelSize>(m, "HasOpNewDelSize").def(py::init<>());
|
||||
py::class_<HasOpNewDelBoth>(m, "HasOpNewDelBoth").def(py::init<>());
|
||||
py::class_<AliasedHasOpNewDelSize, PyAliasedHasOpNewDelSize> aliased(m, "AliasedHasOpNewDelSize");
|
||||
py::class_<AliasedHasOpNewDelSize, PyAliasedHasOpNewDelSize> aliased(m,
|
||||
"AliasedHasOpNewDelSize");
|
||||
aliased.def(py::init<>());
|
||||
aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize));
|
||||
aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize));
|
||||
@ -352,7 +385,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
// [workaround(intel)] = default does not work here
|
||||
// Removing or defaulting this destructor results in linking errors with the Intel compiler
|
||||
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
|
||||
~PublicistB() override {}; // NOLINT(modernize-use-equals-default)
|
||||
~PublicistB() override{}; // NOLINT(modernize-use-equals-default)
|
||||
using ProtectedB::foo;
|
||||
};
|
||||
|
||||
@ -402,8 +435,8 @@ TEST_SUBMODULE(class_, m) {
|
||||
py::class_<Nested>(base, "Nested")
|
||||
.def(py::init<>())
|
||||
.def("fn", [](Nested &, int, NestBase &, Nested &) {})
|
||||
.def("fa", [](Nested &, int, NestBase &, Nested &) {},
|
||||
"a"_a, "b"_a, "c"_a);
|
||||
.def(
|
||||
"fa", [](Nested &, int, NestBase &, Nested &) {}, "a"_a, "b"_a, "c"_a);
|
||||
base.def("g", [](NestBase &, Nested &) {});
|
||||
base.def("h", []() { return NestBase(); });
|
||||
|
||||
@ -413,21 +446,21 @@ TEST_SUBMODULE(class_, m) {
|
||||
// generate a useful error message
|
||||
|
||||
struct NotRegistered {};
|
||||
struct StringWrapper { std::string str; };
|
||||
struct StringWrapper {
|
||||
std::string str;
|
||||
};
|
||||
m.def("test_error_after_conversions", [](int) {});
|
||||
m.def("test_error_after_conversions",
|
||||
[](const StringWrapper &) -> NotRegistered { return {}; });
|
||||
py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
|
||||
py::implicitly_convertible<std::string, StringWrapper>();
|
||||
|
||||
#if defined(PYBIND11_CPP17)
|
||||
struct alignas(1024) Aligned {
|
||||
std::uintptr_t ptr() const { return (uintptr_t) this; }
|
||||
};
|
||||
py::class_<Aligned>(m, "Aligned")
|
||||
.def(py::init<>())
|
||||
.def("ptr", &Aligned::ptr);
|
||||
#endif
|
||||
#if defined(PYBIND11_CPP17)
|
||||
struct alignas(1024) Aligned {
|
||||
std::uintptr_t ptr() const { return (uintptr_t) this; }
|
||||
};
|
||||
py::class_<Aligned>(m, "Aligned").def(py::init<>()).def("ptr", &Aligned::ptr);
|
||||
#endif
|
||||
|
||||
// test_final
|
||||
struct IsFinal final {};
|
||||
@ -440,9 +473,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
// test_exception_rvalue_abort
|
||||
struct PyPrintDestructor {
|
||||
PyPrintDestructor() = default;
|
||||
~PyPrintDestructor() {
|
||||
py::print("Print from destructor");
|
||||
}
|
||||
~PyPrintDestructor() { py::print("Print from destructor"); }
|
||||
void throw_something() { throw std::runtime_error("error"); }
|
||||
};
|
||||
py::class_<PyPrintDestructor>(m, "PyPrintDestructor")
|
||||
@ -456,8 +487,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
.def(py::init([]() { return &samePointer; }));
|
||||
|
||||
struct Empty {};
|
||||
py::class_<Empty>(m, "Empty")
|
||||
.def(py::init<>());
|
||||
py::class_<Empty>(m, "Empty").def(py::init<>());
|
||||
|
||||
// test_base_and_derived_nested_scope
|
||||
struct BaseWithNested {
|
||||
@ -499,12 +529,15 @@ TEST_SUBMODULE(class_, m) {
|
||||
});
|
||||
}
|
||||
|
||||
template <int N> class BreaksBase { public:
|
||||
template <int N>
|
||||
class BreaksBase {
|
||||
public:
|
||||
virtual ~BreaksBase() = default;
|
||||
BreaksBase() = default;
|
||||
BreaksBase(const BreaksBase&) = delete;
|
||||
BreaksBase(const BreaksBase &) = delete;
|
||||
};
|
||||
template <int N> class BreaksTramp : public BreaksBase<N> {};
|
||||
template <int N>
|
||||
class BreaksTramp : public BreaksBase<N> {};
|
||||
// These should all compile just fine:
|
||||
using DoesntBreak1 = py::class_<BreaksBase<1>, std::unique_ptr<BreaksBase<1>>, BreaksTramp<1>>;
|
||||
using DoesntBreak2 = py::class_<BreaksBase<2>, BreaksTramp<2>, std::unique_ptr<BreaksBase<2>>>;
|
||||
@ -514,45 +547,83 @@ using DoesntBreak5 = py::class_<BreaksBase<5>>;
|
||||
using DoesntBreak6 = py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>>;
|
||||
using DoesntBreak7 = py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>>;
|
||||
using DoesntBreak8 = py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>>;
|
||||
#define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<(N)>>::value, \
|
||||
"DoesntBreak" #N " has wrong type!")
|
||||
CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8);
|
||||
#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<(N)>>::value, \
|
||||
#define CHECK_BASE(N) \
|
||||
static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<(N)>>::value, \
|
||||
"DoesntBreak" #N " has wrong type!")
|
||||
CHECK_BASE(1);
|
||||
CHECK_BASE(2);
|
||||
CHECK_BASE(3);
|
||||
CHECK_BASE(4);
|
||||
CHECK_BASE(5);
|
||||
CHECK_BASE(6);
|
||||
CHECK_BASE(7);
|
||||
CHECK_BASE(8);
|
||||
#define CHECK_ALIAS(N) \
|
||||
static_assert( \
|
||||
DoesntBreak##N::has_alias \
|
||||
&& std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<(N)>>::value, \
|
||||
"DoesntBreak" #N " has wrong type_alias!")
|
||||
#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \
|
||||
"DoesntBreak" #N " has type alias, but shouldn't!")
|
||||
CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8);
|
||||
#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<(N)>>>::value, \
|
||||
"DoesntBreak" #N " has wrong holder_type!")
|
||||
CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique);
|
||||
CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
|
||||
#define CHECK_NOALIAS(N) \
|
||||
static_assert(!DoesntBreak##N::has_alias \
|
||||
&& std::is_void<typename DoesntBreak##N::type_alias>::value, \
|
||||
"DoesntBreak" #N " has type alias, but shouldn't!")
|
||||
CHECK_ALIAS(1);
|
||||
CHECK_ALIAS(2);
|
||||
CHECK_NOALIAS(3);
|
||||
CHECK_ALIAS(4);
|
||||
CHECK_NOALIAS(5);
|
||||
CHECK_ALIAS(6);
|
||||
CHECK_ALIAS(7);
|
||||
CHECK_NOALIAS(8);
|
||||
#define CHECK_HOLDER(N, TYPE) \
|
||||
static_assert(std::is_same<typename DoesntBreak##N::holder_type, \
|
||||
std::TYPE##_ptr<BreaksBase<(N)>>>::value, \
|
||||
"DoesntBreak" #N " has wrong holder_type!")
|
||||
CHECK_HOLDER(1, unique);
|
||||
CHECK_HOLDER(2, unique);
|
||||
CHECK_HOLDER(3, unique);
|
||||
CHECK_HOLDER(4, unique);
|
||||
CHECK_HOLDER(5, unique);
|
||||
CHECK_HOLDER(6, shared);
|
||||
CHECK_HOLDER(7, shared);
|
||||
CHECK_HOLDER(8, shared);
|
||||
|
||||
// There's no nice way to test that these fail because they fail to compile; leave them here,
|
||||
// though, so that they can be manually tested by uncommenting them (and seeing that compilation
|
||||
// failures occurs).
|
||||
|
||||
// We have to actually look into the type: the typedef alone isn't enough to instantiate the type:
|
||||
#define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-(N)>>::value, \
|
||||
"Breaks1 has wrong type!");
|
||||
#define CHECK_BROKEN(N) \
|
||||
static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-(N)>>::value, \
|
||||
"Breaks1 has wrong type!");
|
||||
|
||||
#ifdef PYBIND11_NEVER_DEFINED_EVER
|
||||
// Two holder classes:
|
||||
typedef py::class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>> Breaks1;
|
||||
typedef py::
|
||||
class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>>
|
||||
Breaks1;
|
||||
CHECK_BROKEN(1);
|
||||
// Two aliases:
|
||||
typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
|
||||
CHECK_BROKEN(2);
|
||||
// Holder + 2 aliases
|
||||
typedef py::class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>> Breaks3;
|
||||
typedef py::
|
||||
class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>>
|
||||
Breaks3;
|
||||
CHECK_BROKEN(3);
|
||||
// Alias + 2 holders
|
||||
typedef py::class_<BreaksBase<-4>, std::unique_ptr<BreaksBase<-4>>, BreaksTramp<-4>, std::shared_ptr<BreaksBase<-4>>> Breaks4;
|
||||
typedef py::class_<BreaksBase<-4>,
|
||||
std::unique_ptr<BreaksBase<-4>>,
|
||||
BreaksTramp<-4>,
|
||||
std::shared_ptr<BreaksBase<-4>>>
|
||||
Breaks4;
|
||||
CHECK_BROKEN(4);
|
||||
// Invalid option (not a subclass or holder)
|
||||
typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5;
|
||||
CHECK_BROKEN(5);
|
||||
// Invalid option: multiple inheritance not supported:
|
||||
template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
|
||||
template <>
|
||||
struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
|
||||
typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
|
||||
CHECK_BROKEN(8);
|
||||
#endif
|
||||
|
||||
@ -12,20 +12,14 @@
|
||||
|
||||
enum MyEnum { EFirstEntry = 1, ESecondEntry };
|
||||
|
||||
std::string test_function1() {
|
||||
return "test_function()";
|
||||
}
|
||||
std::string test_function1() { return "test_function()"; }
|
||||
|
||||
std::string test_function2(MyEnum k) {
|
||||
return "test_function(enum=" + std::to_string(k) + ")";
|
||||
}
|
||||
std::string test_function2(MyEnum k) { return "test_function(enum=" + std::to_string(k) + ")"; }
|
||||
|
||||
std::string test_function3(int i) {
|
||||
return "test_function(" + std::to_string(i) + ")";
|
||||
}
|
||||
std::string test_function3(int i) { return "test_function(" + std::to_string(i) + ")"; }
|
||||
|
||||
py::str test_function4() { return "test_function()"; }
|
||||
py::str test_function4(char *) { return "test_function(char *)"; }
|
||||
py::str test_function4() { return "test_function()"; }
|
||||
py::str test_function4(char *) { return "test_function(char *)"; }
|
||||
py::str test_function4(int, float) { return "test_function(int, float)"; }
|
||||
py::str test_function4(float, int) { return "test_function(float, int)"; }
|
||||
|
||||
@ -44,50 +38,50 @@ std::string print_bytes(const py::bytes &bytes) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Test that we properly handle C++17 exception specifiers (which are part of the function signature
|
||||
// in C++17). These should all still work before C++17, but don't affect the function signature.
|
||||
// Test that we properly handle C++17 exception specifiers (which are part of the function
|
||||
// signature in C++17). These should all still work before C++17, but don't affect the function
|
||||
// signature.
|
||||
namespace test_exc_sp {
|
||||
// [workaround(intel)] Unable to use noexcept instead of noexcept(true)
|
||||
// Make the f1 test basically the same as the f2 test in C++17 mode for the Intel compiler as
|
||||
// it fails to compile with a plain noexcept (tested with icc (ICC) 2021.1 Beta 20200827).
|
||||
#if defined(__INTEL_COMPILER) && defined(PYBIND11_CPP17)
|
||||
int f1(int x) noexcept(true) { return x+1; }
|
||||
int f1(int x) noexcept(true) { return x + 1; }
|
||||
#else
|
||||
int f1(int x) noexcept { return x+1; }
|
||||
int f1(int x) noexcept { return x + 1; }
|
||||
#endif
|
||||
int f2(int x) noexcept(true) { return x+2; }
|
||||
int f3(int x) noexcept(false) { return x+3; }
|
||||
int f2(int x) noexcept(true) { return x + 2; }
|
||||
int f3(int x) noexcept(false) { return x + 3; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true)
|
||||
int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true)
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
struct C {
|
||||
int m1(int x) noexcept { return x-1; }
|
||||
int m2(int x) const noexcept { return x-2; }
|
||||
int m3(int x) noexcept(true) { return x-3; }
|
||||
int m4(int x) const noexcept(true) { return x-4; }
|
||||
int m5(int x) noexcept(false) { return x-5; }
|
||||
int m6(int x) const noexcept(false) { return x-6; }
|
||||
int m1(int x) noexcept { return x - 1; }
|
||||
int m2(int x) const noexcept { return x - 2; }
|
||||
int m3(int x) noexcept(true) { return x - 3; }
|
||||
int m4(int x) const noexcept(true) { return x - 4; }
|
||||
int m5(int x) noexcept(false) { return x - 5; }
|
||||
int m6(int x) const noexcept(false) { return x - 6; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m7(int x) throw() { return x - 7; }
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m8(int x) const throw() { return x - 8; }
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
};
|
||||
} // namespace test_exc_sp
|
||||
|
||||
|
||||
TEST_SUBMODULE(constants_and_functions, m) {
|
||||
// test_constants
|
||||
m.attr("some_constant") = py::int_(14);
|
||||
@ -129,8 +123,7 @@ TEST_SUBMODULE(constants_and_functions, m) {
|
||||
.def("m5", &C::m5)
|
||||
.def("m6", &C::m6)
|
||||
.def("m7", &C::m7)
|
||||
.def("m8", &C::m8)
|
||||
;
|
||||
.def("m8", &C::m8);
|
||||
m.def("f1", f1);
|
||||
m.def("f2", f2);
|
||||
#if defined(__INTEL_COMPILER)
|
||||
@ -150,8 +143,12 @@ TEST_SUBMODULE(constants_and_functions, m) {
|
||||
uint64_t zeros[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
};
|
||||
m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) {
|
||||
LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here
|
||||
m.def("should_raise", [capture](int) { return capture.zeros[9] + 33; }, py::kw_only(), py::arg());
|
||||
LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here
|
||||
m.def(
|
||||
"should_raise",
|
||||
[capture](int) { return capture.zeros[9] + 33; },
|
||||
py::kw_only(),
|
||||
py::arg());
|
||||
});
|
||||
m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) {
|
||||
m.def(
|
||||
|
||||
@ -8,30 +8,33 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
template <typename derived>
|
||||
struct empty {
|
||||
static const derived& get_one() { return instance_; }
|
||||
static const derived &get_one() { return instance_; }
|
||||
static derived instance_;
|
||||
};
|
||||
|
||||
struct lacking_copy_ctor : public empty<lacking_copy_ctor> {
|
||||
lacking_copy_ctor() = default;
|
||||
lacking_copy_ctor(const lacking_copy_ctor& other) = delete;
|
||||
lacking_copy_ctor(const lacking_copy_ctor &other) = delete;
|
||||
};
|
||||
|
||||
template <> lacking_copy_ctor empty<lacking_copy_ctor>::instance_ = {};
|
||||
template <>
|
||||
lacking_copy_ctor empty<lacking_copy_ctor>::instance_ = {};
|
||||
|
||||
struct lacking_move_ctor : public empty<lacking_move_ctor> {
|
||||
lacking_move_ctor() = default;
|
||||
lacking_move_ctor(const lacking_move_ctor& other) = delete;
|
||||
lacking_move_ctor(lacking_move_ctor&& other) = delete;
|
||||
lacking_move_ctor(const lacking_move_ctor &other) = delete;
|
||||
lacking_move_ctor(lacking_move_ctor &&other) = delete;
|
||||
};
|
||||
|
||||
template <> lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
|
||||
template <>
|
||||
lacking_move_ctor empty<lacking_move_ctor>::instance_ = {};
|
||||
|
||||
/* Custom type caster move/copy test classes */
|
||||
class MoveOnlyInt {
|
||||
@ -71,7 +74,11 @@ public:
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
value = c.value;
|
||||
}
|
||||
MoveOrCopyInt &operator=(const MoveOrCopyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
|
||||
MoveOrCopyInt &operator=(const MoveOrCopyInt &c) {
|
||||
print_copy_assigned(this, c.value);
|
||||
value = c.value;
|
||||
return *this;
|
||||
}
|
||||
~MoveOrCopyInt() { print_destroyed(this); }
|
||||
|
||||
int value;
|
||||
@ -85,32 +92,55 @@ public:
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
value = c.value;
|
||||
}
|
||||
CopyOnlyInt &operator=(const CopyOnlyInt &c) { print_copy_assigned(this, c.value); value = c.value; return *this; }
|
||||
CopyOnlyInt &operator=(const CopyOnlyInt &c) {
|
||||
print_copy_assigned(this, c.value);
|
||||
value = c.value;
|
||||
return *this;
|
||||
}
|
||||
~CopyOnlyInt() { print_destroyed(this); }
|
||||
|
||||
int value;
|
||||
};
|
||||
PYBIND11_NAMESPACE_BEGIN(pybind11)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <> struct type_caster<MoveOnlyInt> {
|
||||
template <>
|
||||
struct type_caster<MoveOnlyInt> {
|
||||
PYBIND11_TYPE_CASTER(MoveOnlyInt, const_name("MoveOnlyInt"));
|
||||
bool load(handle src, bool) { value = MoveOnlyInt(src.cast<int>()); return true; }
|
||||
static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); }
|
||||
bool load(handle src, bool) {
|
||||
value = MoveOnlyInt(src.cast<int>());
|
||||
return true;
|
||||
}
|
||||
static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) {
|
||||
return pybind11::cast(m.value, r, p);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct type_caster<MoveOrCopyInt> {
|
||||
template <>
|
||||
struct type_caster<MoveOrCopyInt> {
|
||||
PYBIND11_TYPE_CASTER(MoveOrCopyInt, const_name("MoveOrCopyInt"));
|
||||
bool load(handle src, bool) { value = MoveOrCopyInt(src.cast<int>()); return true; }
|
||||
static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); }
|
||||
bool load(handle src, bool) {
|
||||
value = MoveOrCopyInt(src.cast<int>());
|
||||
return true;
|
||||
}
|
||||
static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) {
|
||||
return pybind11::cast(m.value, r, p);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct type_caster<CopyOnlyInt> {
|
||||
template <>
|
||||
struct type_caster<CopyOnlyInt> {
|
||||
protected:
|
||||
CopyOnlyInt value;
|
||||
|
||||
public:
|
||||
static constexpr auto name = const_name("CopyOnlyInt");
|
||||
bool load(handle src, bool) { value = CopyOnlyInt(src.cast<int>()); return true; }
|
||||
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); }
|
||||
bool load(handle src, bool) {
|
||||
value = CopyOnlyInt(src.cast<int>());
|
||||
return true;
|
||||
}
|
||||
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) {
|
||||
return pybind11::cast(m.value, r, p);
|
||||
}
|
||||
static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) {
|
||||
if (!src) {
|
||||
return none().release();
|
||||
@ -119,7 +149,8 @@ public:
|
||||
}
|
||||
explicit operator CopyOnlyInt *() { return &value; }
|
||||
explicit operator CopyOnlyInt &() { return value; }
|
||||
template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>;
|
||||
template <typename T>
|
||||
using cast_op_type = pybind11::detail::cast_op_type<T>;
|
||||
};
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
@ -127,23 +158,21 @@ PYBIND11_NAMESPACE_END(pybind11)
|
||||
TEST_SUBMODULE(copy_move_policies, m) {
|
||||
// test_lacking_copy_ctor
|
||||
py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
|
||||
.def_static("get_one", &lacking_copy_ctor::get_one,
|
||||
py::return_value_policy::copy);
|
||||
.def_static("get_one", &lacking_copy_ctor::get_one, py::return_value_policy::copy);
|
||||
// test_lacking_move_ctor
|
||||
py::class_<lacking_move_ctor>(m, "lacking_move_ctor")
|
||||
.def_static("get_one", &lacking_move_ctor::get_one,
|
||||
py::return_value_policy::move);
|
||||
.def_static("get_one", &lacking_move_ctor::get_one, py::return_value_policy::move);
|
||||
|
||||
// test_move_and_copy_casts
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
m.def("move_and_copy_casts", [](const py::object &o) {
|
||||
int r = 0;
|
||||
r += py::cast<MoveOrCopyInt>(o).value; /* moves */
|
||||
r += py::cast<MoveOnlyInt>(o).value; /* moves */
|
||||
r += py::cast<CopyOnlyInt>(o).value; /* copies */
|
||||
auto m1(py::cast<MoveOrCopyInt>(o)); /* moves */
|
||||
auto m2(py::cast<MoveOnlyInt>(o)); /* moves */
|
||||
auto m3(py::cast<CopyOnlyInt>(o)); /* copies */
|
||||
r += py::cast<MoveOnlyInt>(o).value; /* moves */
|
||||
r += py::cast<CopyOnlyInt>(o).value; /* copies */
|
||||
auto m1(py::cast<MoveOrCopyInt>(o)); /* moves */
|
||||
auto m2(py::cast<MoveOnlyInt>(o)); /* moves */
|
||||
auto m3(py::cast<CopyOnlyInt>(o)); /* copies */
|
||||
r += m1.value + m2.value + m3.value;
|
||||
|
||||
return r;
|
||||
@ -157,28 +186,34 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
// Changing this breaks the existing test: needs careful review.
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
|
||||
m.def("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) {
|
||||
return p.first.value + p.second.value;
|
||||
});
|
||||
m.def("move_pair",
|
||||
[](std::pair<MoveOnlyInt, MoveOrCopyInt> p) { return p.first.value + p.second.value; });
|
||||
m.def("move_tuple", [](std::tuple<MoveOnlyInt, MoveOrCopyInt, MoveOnlyInt> t) {
|
||||
return std::get<0>(t).value + std::get<1>(t).value + std::get<2>(t).value;
|
||||
});
|
||||
m.def("copy_tuple", [](std::tuple<CopyOnlyInt, CopyOnlyInt> t) {
|
||||
return std::get<0>(t).value + std::get<1>(t).value;
|
||||
});
|
||||
m.def("move_copy_nested", [](std::pair<MoveOnlyInt, std::pair<std::tuple<MoveOrCopyInt, CopyOnlyInt, std::tuple<MoveOnlyInt>>, MoveOrCopyInt>> x) {
|
||||
return x.first.value + std::get<0>(x.second.first).value + std::get<1>(x.second.first).value +
|
||||
std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value;
|
||||
});
|
||||
m.def("move_copy_nested",
|
||||
[](std::pair<MoveOnlyInt,
|
||||
std::pair<std::tuple<MoveOrCopyInt, CopyOnlyInt, std::tuple<MoveOnlyInt>>,
|
||||
MoveOrCopyInt>> x) {
|
||||
return x.first.value + std::get<0>(x.second.first).value
|
||||
+ std::get<1>(x.second.first).value
|
||||
+ std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value;
|
||||
});
|
||||
m.def("move_and_copy_cstats", []() {
|
||||
ConstructorStats::gc();
|
||||
// Reset counts to 0 so that previous tests don't affect later ones:
|
||||
auto &mc = ConstructorStats::get<MoveOrCopyInt>();
|
||||
mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions = 0;
|
||||
mc.move_assignments = mc.move_constructions = mc.copy_assignments = mc.copy_constructions
|
||||
= 0;
|
||||
auto &mo = ConstructorStats::get<MoveOnlyInt>();
|
||||
mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions = 0;
|
||||
mo.move_assignments = mo.move_constructions = mo.copy_assignments = mo.copy_constructions
|
||||
= 0;
|
||||
auto &co = ConstructorStats::get<CopyOnlyInt>();
|
||||
co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions = 0;
|
||||
co.move_assignments = co.move_constructions = co.copy_assignments = co.copy_constructions
|
||||
= 0;
|
||||
py::dict d;
|
||||
d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference);
|
||||
d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference);
|
||||
@ -188,18 +223,13 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
// test_move_and_copy_load_optional
|
||||
m.attr("has_optional") = true;
|
||||
m.def("move_optional", [](std::optional<MoveOnlyInt> o) {
|
||||
return o->value;
|
||||
});
|
||||
m.def("move_or_copy_optional", [](std::optional<MoveOrCopyInt> o) {
|
||||
return o->value;
|
||||
});
|
||||
m.def("copy_optional", [](std::optional<CopyOnlyInt> o) {
|
||||
return o->value;
|
||||
});
|
||||
m.def("move_optional_tuple", [](std::optional<std::tuple<MoveOrCopyInt, MoveOnlyInt, CopyOnlyInt>> x) {
|
||||
return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value;
|
||||
});
|
||||
m.def("move_optional", [](std::optional<MoveOnlyInt> o) { return o->value; });
|
||||
m.def("move_or_copy_optional", [](std::optional<MoveOrCopyInt> o) { return o->value; });
|
||||
m.def("copy_optional", [](std::optional<CopyOnlyInt> o) { return o->value; });
|
||||
m.def("move_optional_tuple",
|
||||
[](std::optional<std::tuple<MoveOrCopyInt, MoveOnlyInt, CopyOnlyInt>> x) {
|
||||
return std::get<0>(*x).value + std::get<1>(*x).value + std::get<2>(*x).value;
|
||||
});
|
||||
#else
|
||||
m.attr("has_optional") = false;
|
||||
#endif
|
||||
@ -210,6 +240,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
// added later.
|
||||
struct PrivateOpNew {
|
||||
int value = 1;
|
||||
|
||||
private:
|
||||
void *operator new(size_t bytes) {
|
||||
void *ptr = std::malloc(bytes);
|
||||
@ -221,10 +252,13 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
};
|
||||
py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
|
||||
m.def("private_op_new_value", []() { return PrivateOpNew(); });
|
||||
m.def("private_op_new_reference", []() -> const PrivateOpNew & {
|
||||
static PrivateOpNew x{};
|
||||
return x;
|
||||
}, py::return_value_policy::reference);
|
||||
m.def(
|
||||
"private_op_new_reference",
|
||||
[]() -> const PrivateOpNew & {
|
||||
static PrivateOpNew x{};
|
||||
return x;
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// test_move_fallback
|
||||
// #389: rvp::move should fall-through to copy on non-movable objects
|
||||
@ -234,16 +268,25 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
||||
MoveIssue1(const MoveIssue1 &c) = default;
|
||||
MoveIssue1(MoveIssue1 &&) = delete;
|
||||
};
|
||||
py::class_<MoveIssue1>(m, "MoveIssue1").def(py::init<int>()).def_readwrite("value", &MoveIssue1::v);
|
||||
py::class_<MoveIssue1>(m, "MoveIssue1")
|
||||
.def(py::init<int>())
|
||||
.def_readwrite("value", &MoveIssue1::v);
|
||||
|
||||
struct MoveIssue2 {
|
||||
int v;
|
||||
explicit MoveIssue2(int v) : v{v} {}
|
||||
MoveIssue2(MoveIssue2 &&) = default;
|
||||
};
|
||||
py::class_<MoveIssue2>(m, "MoveIssue2").def(py::init<int>()).def_readwrite("value", &MoveIssue2::v);
|
||||
py::class_<MoveIssue2>(m, "MoveIssue2")
|
||||
.def(py::init<int>())
|
||||
.def_readwrite("value", &MoveIssue2::v);
|
||||
|
||||
// #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with `py::return_value_policy::move`
|
||||
m.def("get_moveissue1", [](int i) { return std::unique_ptr<MoveIssue1>(new MoveIssue1(i)); }, py::return_value_policy::move);
|
||||
m.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
|
||||
// #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with
|
||||
// `py::return_value_policy::move`
|
||||
m.def(
|
||||
"get_moveissue1",
|
||||
[](int i) { return std::unique_ptr<MoveIssue1>(new MoveIssue1(i)); },
|
||||
py::return_value_policy::move);
|
||||
m.def(
|
||||
"get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
|
||||
}
|
||||
|
||||
@ -7,16 +7,23 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
// py::arg/py::arg_v testing: these arguments just record their argument when invoked
|
||||
class ArgInspector1 { public: std::string arg = "(default arg inspector 1)"; };
|
||||
class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; };
|
||||
class ArgAlwaysConverts { };
|
||||
namespace pybind11 { namespace detail {
|
||||
template <> struct type_caster<ArgInspector1> {
|
||||
class ArgInspector1 {
|
||||
public:
|
||||
std::string arg = "(default arg inspector 1)";
|
||||
};
|
||||
class ArgInspector2 {
|
||||
public:
|
||||
std::string arg = "(default arg inspector 2)";
|
||||
};
|
||||
class ArgAlwaysConverts {};
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<ArgInspector1> {
|
||||
public:
|
||||
// Classic
|
||||
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
@ -26,9 +33,10 @@ public:
|
||||
#endif
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
value.arg = "loading ArgInspector1 argument " +
|
||||
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
|
||||
"Argument value = " + (std::string) str(src);
|
||||
value.arg = "loading ArgInspector1 argument " + std::string(convert ? "WITH" : "WITHOUT")
|
||||
+ " conversion allowed. "
|
||||
"Argument value = "
|
||||
+ (std::string) str(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -36,14 +44,16 @@ public:
|
||||
return str(src.arg).release();
|
||||
}
|
||||
};
|
||||
template <> struct type_caster<ArgInspector2> {
|
||||
template <>
|
||||
struct type_caster<ArgInspector2> {
|
||||
public:
|
||||
PYBIND11_TYPE_CASTER(ArgInspector2, const_name("ArgInspector2"));
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
value.arg = "loading ArgInspector2 argument " +
|
||||
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. "
|
||||
"Argument value = " + (std::string) str(src);
|
||||
value.arg = "loading ArgInspector2 argument " + std::string(convert ? "WITH" : "WITHOUT")
|
||||
+ " conversion allowed. "
|
||||
"Argument value = "
|
||||
+ (std::string) str(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -51,13 +61,12 @@ public:
|
||||
return str(src.arg).release();
|
||||
}
|
||||
};
|
||||
template <> struct type_caster<ArgAlwaysConverts> {
|
||||
template <>
|
||||
struct type_caster<ArgAlwaysConverts> {
|
||||
public:
|
||||
PYBIND11_TYPE_CASTER(ArgAlwaysConverts, const_name("ArgAlwaysConverts"));
|
||||
|
||||
bool load(handle, bool convert) {
|
||||
return convert;
|
||||
}
|
||||
bool load(handle, bool convert) { return convert; }
|
||||
|
||||
static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) {
|
||||
return py::none().release();
|
||||
@ -73,14 +82,19 @@ public:
|
||||
~DestructionTester() { print_destroyed(this); }
|
||||
DestructionTester(const DestructionTester &) { print_copy_created(this); }
|
||||
DestructionTester(DestructionTester &&) noexcept { print_move_created(this); }
|
||||
DestructionTester &operator=(const DestructionTester &) { print_copy_assigned(this); return *this; }
|
||||
DestructionTester &operator=(const DestructionTester &) {
|
||||
print_copy_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
DestructionTester &operator=(DestructionTester &&) noexcept {
|
||||
print_move_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
namespace pybind11 { namespace detail {
|
||||
template <> struct type_caster<DestructionTester> {
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<DestructionTester> {
|
||||
PYBIND11_TYPE_CASTER(DestructionTester, const_name("DestructionTester"));
|
||||
bool load(handle, bool) { return true; }
|
||||
|
||||
@ -115,9 +129,14 @@ TEST_SUBMODULE(custom_type_casters, m) {
|
||||
py::class_<ArgInspector>(m, "ArgInspector")
|
||||
.def(py::init<>())
|
||||
.def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts())
|
||||
.def("g", &ArgInspector::g, "a"_a.noconvert(), "b"_a, "c"_a.noconvert()=13, "d"_a=ArgInspector2(), py::arg() = ArgAlwaysConverts())
|
||||
.def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts())
|
||||
;
|
||||
.def("g",
|
||||
&ArgInspector::g,
|
||||
"a"_a.noconvert(),
|
||||
"b"_a,
|
||||
"c"_a.noconvert() = 13,
|
||||
"d"_a = ArgInspector2(),
|
||||
py::arg() = ArgAlwaysConverts())
|
||||
.def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts());
|
||||
m.def(
|
||||
"arg_inspect_func",
|
||||
[](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) {
|
||||
@ -127,20 +146,33 @@ TEST_SUBMODULE(custom_type_casters, m) {
|
||||
py::arg_v(nullptr, ArgInspector1()).noconvert(true),
|
||||
py::arg() = ArgAlwaysConverts());
|
||||
|
||||
m.def("floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
|
||||
m.def("floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());
|
||||
m.def("ints_preferred", [](int i) { return i / 2; }, "i"_a);
|
||||
m.def("ints_only", [](int i) { return i / 2; }, "i"_a.noconvert());
|
||||
m.def(
|
||||
"floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
|
||||
m.def(
|
||||
"floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());
|
||||
m.def(
|
||||
"ints_preferred", [](int i) { return i / 2; }, "i"_a);
|
||||
m.def(
|
||||
"ints_only", [](int i) { return i / 2; }, "i"_a.noconvert());
|
||||
|
||||
// test_custom_caster_destruction
|
||||
// Test that `take_ownership` works on types with a custom type caster when given a pointer
|
||||
|
||||
// default policy: don't take ownership:
|
||||
m.def("custom_caster_no_destroy", []() { static auto *dt = new DestructionTester(); return dt; });
|
||||
m.def("custom_caster_no_destroy", []() {
|
||||
static auto *dt = new DestructionTester();
|
||||
return dt;
|
||||
});
|
||||
|
||||
m.def("custom_caster_destroy", []() { return new DestructionTester(); },
|
||||
py::return_value_policy::take_ownership); // Takes ownership: destroy when finished
|
||||
m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); },
|
||||
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
|
||||
m.def("destruction_tester_cstats", &ConstructorStats::get<DestructionTester>, py::return_value_policy::reference);
|
||||
m.def(
|
||||
"custom_caster_destroy",
|
||||
[]() { return new DestructionTester(); },
|
||||
py::return_value_policy::take_ownership); // Takes ownership: destroy when finished
|
||||
m.def(
|
||||
"custom_caster_destroy_const",
|
||||
[]() -> const DestructionTester * { return new DestructionTester(); },
|
||||
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
|
||||
m.def("destruction_tester_cstats",
|
||||
&ConstructorStats::get<DestructionTester>,
|
||||
py::return_value_policy::reference);
|
||||
}
|
||||
|
||||
@ -15,35 +15,52 @@ TEST_SUBMODULE(docstring_options, m) {
|
||||
py::options options;
|
||||
options.disable_function_signatures();
|
||||
|
||||
m.def("test_function1", [](int, int) {}, py::arg("a"), py::arg("b"));
|
||||
m.def("test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
m.def(
|
||||
"test_function1", [](int, int) {}, py::arg("a"), py::arg("b"));
|
||||
m.def(
|
||||
"test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
|
||||
m.def("test_overloaded1", [](int) {}, py::arg("i"), "Overload docstring");
|
||||
m.def("test_overloaded1", [](double) {}, py::arg("d"));
|
||||
m.def(
|
||||
"test_overloaded1", [](int) {}, py::arg("i"), "Overload docstring");
|
||||
m.def(
|
||||
"test_overloaded1", [](double) {}, py::arg("d"));
|
||||
|
||||
m.def("test_overloaded2", [](int) {}, py::arg("i"), "overload docstring 1");
|
||||
m.def("test_overloaded2", [](double) {}, py::arg("d"), "overload docstring 2");
|
||||
m.def(
|
||||
"test_overloaded2", [](int) {}, py::arg("i"), "overload docstring 1");
|
||||
m.def(
|
||||
"test_overloaded2", [](double) {}, py::arg("d"), "overload docstring 2");
|
||||
|
||||
m.def("test_overloaded3", [](int) {}, py::arg("i"));
|
||||
m.def("test_overloaded3", [](double) {}, py::arg("d"), "Overload docstr");
|
||||
m.def(
|
||||
"test_overloaded3", [](int) {}, py::arg("i"));
|
||||
m.def(
|
||||
"test_overloaded3", [](double) {}, py::arg("d"), "Overload docstr");
|
||||
|
||||
options.enable_function_signatures();
|
||||
|
||||
m.def("test_function3", [](int, int) {}, py::arg("a"), py::arg("b"));
|
||||
m.def("test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
m.def(
|
||||
"test_function3", [](int, int) {}, py::arg("a"), py::arg("b"));
|
||||
m.def(
|
||||
"test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
|
||||
options.disable_function_signatures().disable_user_defined_docstrings();
|
||||
|
||||
m.def("test_function5", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
m.def(
|
||||
"test_function5", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
|
||||
{
|
||||
py::options nested_options;
|
||||
nested_options.enable_user_defined_docstrings();
|
||||
m.def("test_function6", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
m.def(
|
||||
"test_function6",
|
||||
[](int, int) {},
|
||||
py::arg("a"),
|
||||
py::arg("b"),
|
||||
"A custom docstring");
|
||||
}
|
||||
}
|
||||
|
||||
m.def("test_function7", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
m.def(
|
||||
"test_function7", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||
|
||||
{
|
||||
py::options options;
|
||||
@ -63,7 +80,9 @@ TEST_SUBMODULE(docstring_options, m) {
|
||||
int getValue() const { return value; }
|
||||
};
|
||||
py::class_<DocstringTestFoo>(m, "DocstringTestFoo", "This is a class docstring")
|
||||
.def_property("value_prop", &DocstringTestFoo::getValue, &DocstringTestFoo::setValue, "This is a property docstring")
|
||||
;
|
||||
.def_property("value_prop",
|
||||
&DocstringTestFoo::getValue,
|
||||
&DocstringTestFoo::setValue,
|
||||
"This is a property docstring");
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,27 +7,27 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/eigen.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if _MSC_VER < 1910 // VS 2015's MSVC
|
||||
# pragma warning(disable: 4127) // C4127: conditional expression is constant
|
||||
#endif
|
||||
# pragma warning(disable: 4996) // C4996: std::unary_negation is deprecated
|
||||
# if _MSC_VER < 1910 // VS 2015's MSVC
|
||||
# pragma warning(disable : 4127) // C4127: conditional expression is constant
|
||||
# endif
|
||||
# pragma warning(disable : 4996) // C4996: std::unary_negation is deprecated
|
||||
#endif
|
||||
|
||||
#include <Eigen/Cholesky>
|
||||
|
||||
using MatrixXdR = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
|
||||
|
||||
|
||||
|
||||
// Sets/resets a testing reference matrix to have values of 10*r + c, where r and c are the
|
||||
// (1-based) row/column number.
|
||||
template <typename M> void reset_ref(M &x) {
|
||||
template <typename M>
|
||||
void reset_ref(M &x) {
|
||||
for (int i = 0; i < x.rows(); i++) {
|
||||
for (int j = 0; j < x.cols(); j++) {
|
||||
x(i, j) = 11 + 10 * i + j;
|
||||
@ -64,7 +64,8 @@ double get_elem(const Eigen::Ref<const Eigen::MatrixXd> &m) { return m(2, 1); };
|
||||
|
||||
// Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix
|
||||
// reference is referencing rows/columns correctly).
|
||||
template <typename MatrixArgType> Eigen::MatrixXd adjust_matrix(MatrixArgType m) {
|
||||
template <typename MatrixArgType>
|
||||
Eigen::MatrixXd adjust_matrix(MatrixArgType m) {
|
||||
Eigen::MatrixXd ret(m);
|
||||
for (int c = 0; c < m.cols(); c++) {
|
||||
for (int r = 0; r < m.rows(); r++) {
|
||||
@ -97,8 +98,10 @@ TEST_SUBMODULE(eigen, m) {
|
||||
|
||||
// various tests
|
||||
m.def("double_col", [](const Eigen::VectorXf &x) -> Eigen::VectorXf { return 2.0f * x; });
|
||||
m.def("double_row", [](const Eigen::RowVectorXf &x) -> Eigen::RowVectorXf { return 2.0f * x; });
|
||||
m.def("double_complex", [](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; });
|
||||
m.def("double_row",
|
||||
[](const Eigen::RowVectorXf &x) -> Eigen::RowVectorXf { return 2.0f * x; });
|
||||
m.def("double_complex",
|
||||
[](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; });
|
||||
m.def("double_threec", [](py::EigenDRef<Eigen::Vector3f> x) { x *= 2; });
|
||||
m.def("double_threer", [](py::EigenDRef<Eigen::RowVector3f> x) { x *= 2; });
|
||||
m.def("double_mat_cm", [](const Eigen::MatrixXf &x) -> Eigen::MatrixXf { return 2.0f * x; });
|
||||
@ -108,19 +111,22 @@ TEST_SUBMODULE(eigen, m) {
|
||||
// Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended
|
||||
m.def("cholesky1",
|
||||
[](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
|
||||
m.def("cholesky2", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
|
||||
m.def("cholesky3", [](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
|
||||
m.def("cholesky2", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd {
|
||||
return x.llt().matrixL();
|
||||
});
|
||||
m.def("cholesky3",
|
||||
[](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); });
|
||||
m.def("cholesky4", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd {
|
||||
return x.llt().matrixL();
|
||||
});
|
||||
|
||||
// test_eigen_ref_mutators
|
||||
// Mutators: these add some value to the given element using Eigen, but Eigen should be mapping into
|
||||
// the numpy array data and so the result should show up there. There are three versions: one that
|
||||
// works on a contiguous-row matrix (numpy's default), one for a contiguous-column matrix, and one
|
||||
// for any matrix.
|
||||
auto add_rm = [](Eigen::Ref<MatrixXdR> x, int r, int c, double v) { x(r,c) += v; };
|
||||
auto add_cm = [](Eigen::Ref<Eigen::MatrixXd> x, int r, int c, double v) { x(r,c) += v; };
|
||||
// Mutators: these add some value to the given element using Eigen, but Eigen should be mapping
|
||||
// into the numpy array data and so the result should show up there. There are three versions:
|
||||
// one that works on a contiguous-row matrix (numpy's default), one for a contiguous-column
|
||||
// matrix, and one for any matrix.
|
||||
auto add_rm = [](Eigen::Ref<MatrixXdR> x, int r, int c, double v) { x(r, c) += v; };
|
||||
auto add_cm = [](Eigen::Ref<Eigen::MatrixXd> x, int r, int c, double v) { x(r, c) += v; };
|
||||
|
||||
// Mutators (Eigen maps into numpy variables):
|
||||
m.def("add_rm", add_rm); // Only takes row-contiguous
|
||||
@ -131,7 +137,8 @@ TEST_SUBMODULE(eigen, m) {
|
||||
m.def("add2", add_cm);
|
||||
m.def("add2", add_rm);
|
||||
// This one accepts a matrix of any stride:
|
||||
m.def("add_any", [](py::EigenDRef<Eigen::MatrixXd> x, int r, int c, double v) { x(r,c) += v; });
|
||||
m.def("add_any",
|
||||
[](py::EigenDRef<Eigen::MatrixXd> x, int r, int c, double v) { x(r, c) += v; });
|
||||
|
||||
// Return mutable references (numpy maps into eigen variables)
|
||||
m.def("get_cm_ref", []() { return Eigen::Ref<Eigen::MatrixXd>(get_cm()); });
|
||||
@ -143,45 +150,67 @@ TEST_SUBMODULE(eigen, m) {
|
||||
m.def("reset_refs", reset_refs); // Restores get_{cm,rm}_ref to original values
|
||||
|
||||
// Increments and returns ref to (same) matrix
|
||||
m.def("incr_matrix", [](Eigen::Ref<Eigen::MatrixXd> m, double v) {
|
||||
m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v);
|
||||
return m;
|
||||
}, py::return_value_policy::reference);
|
||||
m.def(
|
||||
"incr_matrix",
|
||||
[](Eigen::Ref<Eigen::MatrixXd> m, double v) {
|
||||
m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v);
|
||||
return m;
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// Same, but accepts a matrix of any strides
|
||||
m.def("incr_matrix_any", [](py::EigenDRef<Eigen::MatrixXd> m, double v) {
|
||||
m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v);
|
||||
return m;
|
||||
}, py::return_value_policy::reference);
|
||||
m.def(
|
||||
"incr_matrix_any",
|
||||
[](py::EigenDRef<Eigen::MatrixXd> m, double v) {
|
||||
m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v);
|
||||
return m;
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// Returns an eigen slice of even rows
|
||||
m.def("even_rows", [](py::EigenDRef<Eigen::MatrixXd> m) {
|
||||
return py::EigenDMap<Eigen::MatrixXd>(
|
||||
m.data(), (m.rows() + 1) / 2, m.cols(),
|
||||
m.def(
|
||||
"even_rows",
|
||||
[](py::EigenDRef<Eigen::MatrixXd> m) {
|
||||
return py::EigenDMap<Eigen::MatrixXd>(
|
||||
m.data(),
|
||||
(m.rows() + 1) / 2,
|
||||
m.cols(),
|
||||
py::EigenDStride(m.outerStride(), 2 * m.innerStride()));
|
||||
}, py::return_value_policy::reference);
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// Returns an eigen slice of even columns
|
||||
m.def("even_cols", [](py::EigenDRef<Eigen::MatrixXd> m) {
|
||||
return py::EigenDMap<Eigen::MatrixXd>(
|
||||
m.data(), m.rows(), (m.cols() + 1) / 2,
|
||||
m.def(
|
||||
"even_cols",
|
||||
[](py::EigenDRef<Eigen::MatrixXd> m) {
|
||||
return py::EigenDMap<Eigen::MatrixXd>(
|
||||
m.data(),
|
||||
m.rows(),
|
||||
(m.cols() + 1) / 2,
|
||||
py::EigenDStride(2 * m.outerStride(), m.innerStride()));
|
||||
}, py::return_value_policy::reference);
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
|
||||
// Returns diagonals: a vector-like object with an inner stride != 1
|
||||
m.def("diagonal", [](const Eigen::Ref<const Eigen::MatrixXd> &x) { return x.diagonal(); });
|
||||
m.def("diagonal_1", [](const Eigen::Ref<const Eigen::MatrixXd> &x) { return x.diagonal<1>(); });
|
||||
m.def("diagonal_n", [](const Eigen::Ref<const Eigen::MatrixXd> &x, int index) { return x.diagonal(index); });
|
||||
m.def("diagonal_1",
|
||||
[](const Eigen::Ref<const Eigen::MatrixXd> &x) { return x.diagonal<1>(); });
|
||||
m.def("diagonal_n",
|
||||
[](const Eigen::Ref<const Eigen::MatrixXd> &x, int index) { return x.diagonal(index); });
|
||||
|
||||
// Return a block of a matrix (gives non-standard strides)
|
||||
m.def("block", [](const Eigen::Ref<const Eigen::MatrixXd> &x, int start_row, int start_col, int block_rows, int block_cols) {
|
||||
return x.block(start_row, start_col, block_rows, block_cols);
|
||||
});
|
||||
m.def("block",
|
||||
[](const Eigen::Ref<const Eigen::MatrixXd> &x,
|
||||
int start_row,
|
||||
int start_col,
|
||||
int block_rows,
|
||||
int block_cols) { return x.block(start_row, start_col, block_rows, block_cols); });
|
||||
|
||||
// test_eigen_return_references, test_eigen_keepalive
|
||||
// return value referencing/copying tests:
|
||||
class ReturnTester {
|
||||
Eigen::MatrixXd mat = create();
|
||||
|
||||
public:
|
||||
ReturnTester() { print_created(this); }
|
||||
~ReturnTester() { print_destroyed(this); }
|
||||
@ -194,12 +223,24 @@ TEST_SUBMODULE(eigen, m) {
|
||||
const Eigen::MatrixXd *viewPtr() { return &mat; }
|
||||
Eigen::Ref<Eigen::MatrixXd> ref() { return mat; }
|
||||
Eigen::Ref<const Eigen::MatrixXd> refConst() { return mat; }
|
||||
Eigen::Block<Eigen::MatrixXd> block(int r, int c, int nrow, int ncol) { return mat.block(r, c, nrow, ncol); }
|
||||
Eigen::Block<const Eigen::MatrixXd> blockConst(int r, int c, int nrow, int ncol) const { return mat.block(r, c, nrow, ncol); }
|
||||
py::EigenDMap<Eigen::Matrix2d> corners() { return py::EigenDMap<Eigen::Matrix2d>(mat.data(),
|
||||
py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); }
|
||||
py::EigenDMap<const Eigen::Matrix2d> cornersConst() const { return py::EigenDMap<const Eigen::Matrix2d>(mat.data(),
|
||||
py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); }
|
||||
Eigen::Block<Eigen::MatrixXd> block(int r, int c, int nrow, int ncol) {
|
||||
return mat.block(r, c, nrow, ncol);
|
||||
}
|
||||
Eigen::Block<const Eigen::MatrixXd> blockConst(int r, int c, int nrow, int ncol) const {
|
||||
return mat.block(r, c, nrow, ncol);
|
||||
}
|
||||
py::EigenDMap<Eigen::Matrix2d> corners() {
|
||||
return py::EigenDMap<Eigen::Matrix2d>(
|
||||
mat.data(),
|
||||
py::EigenDStride(mat.outerStride() * (mat.outerSize() - 1),
|
||||
mat.innerStride() * (mat.innerSize() - 1)));
|
||||
}
|
||||
py::EigenDMap<const Eigen::Matrix2d> cornersConst() const {
|
||||
return py::EigenDMap<const Eigen::Matrix2d>(
|
||||
mat.data(),
|
||||
py::EigenDStride(mat.outerStride() * (mat.outerSize() - 1),
|
||||
mat.innerStride() * (mat.innerSize() - 1)));
|
||||
}
|
||||
};
|
||||
using rvp = py::return_value_policy;
|
||||
py::class_<ReturnTester>(m, "ReturnTester")
|
||||
@ -210,9 +251,9 @@ TEST_SUBMODULE(eigen, m) {
|
||||
.def("get_ptr", &ReturnTester::getPtr, rvp::reference_internal)
|
||||
.def("view", &ReturnTester::view, rvp::reference_internal)
|
||||
.def("view_ptr", &ReturnTester::view, rvp::reference_internal)
|
||||
.def("copy_get", &ReturnTester::get) // Default rvp: copy
|
||||
.def("copy_view", &ReturnTester::view) // "
|
||||
.def("ref", &ReturnTester::ref) // Default for Ref is to reference
|
||||
.def("copy_get", &ReturnTester::get) // Default rvp: copy
|
||||
.def("copy_view", &ReturnTester::view) // "
|
||||
.def("ref", &ReturnTester::ref) // Default for Ref is to reference
|
||||
.def("ref_const", &ReturnTester::refConst) // Likewise, but const
|
||||
.def("ref_safe", &ReturnTester::ref, rvp::reference_internal)
|
||||
.def("ref_const_safe", &ReturnTester::refConst, rvp::reference_internal)
|
||||
@ -223,8 +264,7 @@ TEST_SUBMODULE(eigen, m) {
|
||||
.def("block_const", &ReturnTester::blockConst, rvp::reference_internal)
|
||||
.def("copy_block", &ReturnTester::block, rvp::copy)
|
||||
.def("corners", &ReturnTester::corners, rvp::reference_internal)
|
||||
.def("corners_const", &ReturnTester::cornersConst, rvp::reference_internal)
|
||||
;
|
||||
.def("corners_const", &ReturnTester::cornersConst, rvp::reference_internal);
|
||||
|
||||
// test_special_matrix_objects
|
||||
// Returns a DiagonalMatrix with diagonal (1,2,3,...)
|
||||
@ -237,21 +277,16 @@ TEST_SUBMODULE(eigen, m) {
|
||||
});
|
||||
|
||||
// Returns a SelfAdjointView referencing the lower triangle of m
|
||||
m.def("symmetric_lower", [](const Eigen::MatrixXi &m) {
|
||||
return m.selfadjointView<Eigen::Lower>();
|
||||
});
|
||||
m.def("symmetric_lower",
|
||||
[](const Eigen::MatrixXi &m) { return m.selfadjointView<Eigen::Lower>(); });
|
||||
// Returns a SelfAdjointView referencing the lower triangle of m
|
||||
m.def("symmetric_upper", [](const Eigen::MatrixXi &m) {
|
||||
return m.selfadjointView<Eigen::Upper>();
|
||||
});
|
||||
m.def("symmetric_upper",
|
||||
[](const Eigen::MatrixXi &m) { return m.selfadjointView<Eigen::Upper>(); });
|
||||
|
||||
// Test matrix for various functions below.
|
||||
Eigen::MatrixXf mat(5, 6);
|
||||
mat << 0, 3, 0, 0, 0, 11,
|
||||
22, 0, 0, 0, 17, 11,
|
||||
7, 5, 0, 1, 0, 11,
|
||||
0, 0, 0, 0, 0, 11,
|
||||
0, 0, 14, 0, 8, 11;
|
||||
mat << 0, 3, 0, 0, 0, 11, 22, 0, 0, 0, 17, 11, 7, 5, 0, 1, 0, 11, 0, 0, 0, 0, 0, 11, 0, 0, 14,
|
||||
0, 8, 11;
|
||||
|
||||
// test_fixed, and various other tests
|
||||
m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); });
|
||||
@ -276,7 +311,8 @@ TEST_SUBMODULE(eigen, m) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
||||
return Eigen::SparseView<Eigen::MatrixXf>(mat);
|
||||
});
|
||||
m.def("sparse_c", [mat]() -> SparseMatrixC { return Eigen::SparseView<Eigen::MatrixXf>(mat); });
|
||||
m.def("sparse_c",
|
||||
[mat]() -> SparseMatrixC { return Eigen::SparseView<Eigen::MatrixXf>(mat); });
|
||||
m.def("sparse_copy_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; });
|
||||
m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; });
|
||||
// test_partially_fixed
|
||||
@ -290,7 +326,8 @@ TEST_SUBMODULE(eigen, m) {
|
||||
m.def("cpp_copy", [](py::handle m) { return m.cast<Eigen::MatrixXd>()(1, 0); });
|
||||
m.def("cpp_ref_c", [](py::handle m) { return m.cast<Eigen::Ref<Eigen::MatrixXd>>()(1, 0); });
|
||||
m.def("cpp_ref_r", [](py::handle m) { return m.cast<Eigen::Ref<MatrixXdR>>()(1, 0); });
|
||||
m.def("cpp_ref_any", [](py::handle m) { return m.cast<py::EigenDRef<Eigen::MatrixXd>>()(1, 0); });
|
||||
m.def("cpp_ref_any",
|
||||
[](py::handle m) { return m.cast<py::EigenDRef<Eigen::MatrixXd>>()(1, 0); });
|
||||
|
||||
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
|
||||
|
||||
@ -304,15 +341,23 @@ TEST_SUBMODULE(eigen, m) {
|
||||
[](const Eigen::Ref<const Eigen::MatrixXd> &m) -> double { return get_elem(m); },
|
||||
py::arg{}.noconvert());
|
||||
// Also test a row-major-only no-copy const ref:
|
||||
m.def("get_elem_rm_nocopy", [](Eigen::Ref<const Eigen::Matrix<long, -1, -1, Eigen::RowMajor>> &m) -> long { return m(2, 1); },
|
||||
py::arg{}.noconvert());
|
||||
m.def(
|
||||
"get_elem_rm_nocopy",
|
||||
[](Eigen::Ref<const Eigen::Matrix<long, -1, -1, Eigen::RowMajor>> &m) -> long {
|
||||
return m(2, 1);
|
||||
},
|
||||
py::arg{}.noconvert());
|
||||
|
||||
// test_issue738
|
||||
// Issue #738: 1xN or Nx1 2D matrices were neither accepted nor properly copied with an
|
||||
// incompatible stride value on the length-1 dimension--but that should be allowed (without
|
||||
// requiring a copy!) because the stride value can be safely ignored on a size-1 dimension.
|
||||
m.def("iss738_f1", &adjust_matrix<const Eigen::Ref<const Eigen::MatrixXd> &>, py::arg{}.noconvert());
|
||||
m.def("iss738_f2", &adjust_matrix<const Eigen::Ref<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>> &>, py::arg{}.noconvert());
|
||||
m.def("iss738_f1",
|
||||
&adjust_matrix<const Eigen::Ref<const Eigen::MatrixXd> &>,
|
||||
py::arg{}.noconvert());
|
||||
m.def("iss738_f2",
|
||||
&adjust_matrix<const Eigen::Ref<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>> &>,
|
||||
py::arg{}.noconvert());
|
||||
|
||||
// test_issue1105
|
||||
// Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense
|
||||
|
||||
@ -4,14 +4,14 @@
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch
|
||||
// 2.0.1; this should be fixed in the next catch release after 2.0.1).
|
||||
# pragma warning(disable: 4996)
|
||||
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
|
||||
// catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
|
||||
# pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
// Catch uses _ internally, which breaks gettext style defines
|
||||
#ifdef _
|
||||
#undef _
|
||||
# undef _
|
||||
#endif
|
||||
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
|
||||
@ -13,11 +13,8 @@ PYBIND11_MODULE(external_module, m) {
|
||||
int v;
|
||||
};
|
||||
|
||||
py::class_<A>(m, "A")
|
||||
.def(py::init<int>())
|
||||
.def_readwrite("value", &A::v);
|
||||
py::class_<A>(m, "A").def(py::init<int>()).def_readwrite("value", &A::v);
|
||||
|
||||
m.def("internals_at", []() {
|
||||
return reinterpret_cast<uintptr_t>(&py::detail::get_internals());
|
||||
});
|
||||
m.def("internals_at",
|
||||
[]() { return reinterpret_cast<uintptr_t>(&py::detail::get_internals()); });
|
||||
}
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch
|
||||
// 2.0.1; this should be fixed in the next catch release after 2.0.1).
|
||||
# pragma warning(disable: 4996)
|
||||
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
|
||||
// catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
|
||||
# pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
@ -62,14 +61,14 @@ PYBIND11_EMBEDDED_MODULE(widget_module, m) {
|
||||
}
|
||||
|
||||
PYBIND11_EMBEDDED_MODULE(trampoline_module, m) {
|
||||
py::class_<test_override_cache_helper, test_override_cache_helper_trampoline, std::shared_ptr<test_override_cache_helper>>(m, "test_override_cache_helper")
|
||||
py::class_<test_override_cache_helper,
|
||||
test_override_cache_helper_trampoline,
|
||||
std::shared_ptr<test_override_cache_helper>>(m, "test_override_cache_helper")
|
||||
.def(py::init_alias<>())
|
||||
.def("func", &test_override_cache_helper::func);
|
||||
}
|
||||
|
||||
PYBIND11_EMBEDDED_MODULE(throw_exception, ) {
|
||||
throw std::runtime_error("C++ Error");
|
||||
}
|
||||
PYBIND11_EMBEDDED_MODULE(throw_exception, ) { throw std::runtime_error("C++ Error"); }
|
||||
|
||||
PYBIND11_EMBEDDED_MODULE(throw_error_already_set, ) {
|
||||
auto d = py::dict();
|
||||
@ -80,11 +79,13 @@ TEST_CASE("Pass classes and data between modules defined in C++ and Python") {
|
||||
auto module_ = py::module_::import("test_interpreter");
|
||||
REQUIRE(py::hasattr(module_, "DerivedWidget"));
|
||||
|
||||
auto locals = py::dict("hello"_a="Hello, World!", "x"_a=5, **module_.attr("__dict__"));
|
||||
auto locals = py::dict("hello"_a = "Hello, World!", "x"_a = 5, **module_.attr("__dict__"));
|
||||
py::exec(R"(
|
||||
widget = DerivedWidget("{} - {}".format(hello, x))
|
||||
message = widget.the_message
|
||||
)", py::globals(), locals);
|
||||
)",
|
||||
py::globals(),
|
||||
locals);
|
||||
REQUIRE(locals["message"].cast<std::string>() == "Hello, World! - 5");
|
||||
|
||||
auto py_widget = module_.attr("DerivedWidget")("The question");
|
||||
@ -124,20 +125,21 @@ TEST_CASE("Override cache") {
|
||||
|
||||
TEST_CASE("Import error handling") {
|
||||
REQUIRE_NOTHROW(py::module_::import("widget_module"));
|
||||
REQUIRE_THROWS_WITH(py::module_::import("throw_exception"),
|
||||
"ImportError: C++ Error");
|
||||
REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), "ImportError: C++ Error");
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
|
||||
Catch::Contains("ImportError: initialization failed"));
|
||||
|
||||
auto locals = py::dict("is_keyerror"_a=false, "message"_a="not set");
|
||||
auto locals = py::dict("is_keyerror"_a = false, "message"_a = "not set");
|
||||
py::exec(R"(
|
||||
try:
|
||||
import throw_error_already_set
|
||||
except ImportError as e:
|
||||
is_keyerror = type(e.__cause__) == KeyError
|
||||
message = str(e.__cause__)
|
||||
)", py::globals(), locals);
|
||||
)",
|
||||
py::globals(),
|
||||
locals);
|
||||
REQUIRE(locals["is_keyerror"].cast<bool>() == true);
|
||||
REQUIRE(locals["message"].cast<std::string>() == "'missing'");
|
||||
#else
|
||||
@ -179,11 +181,12 @@ TEST_CASE("Restart the interpreter") {
|
||||
REQUIRE(py::module_::import("widget_module").attr("add")(1, 2).cast<int>() == 3);
|
||||
REQUIRE(has_pybind11_internals_builtin());
|
||||
REQUIRE(has_pybind11_internals_static());
|
||||
REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast<int>() == 123);
|
||||
REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast<int>()
|
||||
== 123);
|
||||
|
||||
// local and foreign module internals should point to the same internals:
|
||||
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) ==
|
||||
py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>());
|
||||
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp())
|
||||
== py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>());
|
||||
|
||||
// Restart the interpreter.
|
||||
py::finalize_interpreter();
|
||||
@ -198,16 +201,19 @@ TEST_CASE("Restart the interpreter") {
|
||||
pybind11::detail::get_internals();
|
||||
REQUIRE(has_pybind11_internals_builtin());
|
||||
REQUIRE(has_pybind11_internals_static());
|
||||
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) ==
|
||||
py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>());
|
||||
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp())
|
||||
== py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>());
|
||||
|
||||
// Make sure that an interpreter with no get_internals() created until finalize still gets the
|
||||
// internals destroyed
|
||||
py::finalize_interpreter();
|
||||
py::initialize_interpreter();
|
||||
bool ran = false;
|
||||
py::module_::import("__main__").attr("internals_destroy_test") =
|
||||
py::capsule(&ran, [](void *ran) { py::detail::get_internals(); *static_cast<bool *>(ran) = true; });
|
||||
py::module_::import("__main__").attr("internals_destroy_test")
|
||||
= py::capsule(&ran, [](void *ran) {
|
||||
py::detail::get_internals();
|
||||
*static_cast<bool *>(ran) = true;
|
||||
});
|
||||
REQUIRE_FALSE(has_pybind11_internals_builtin());
|
||||
REQUIRE_FALSE(has_pybind11_internals_static());
|
||||
REQUIRE_FALSE(ran);
|
||||
@ -281,7 +287,7 @@ TEST_CASE("Threads") {
|
||||
REQUIRE_FALSE(has_pybind11_internals_static());
|
||||
|
||||
constexpr auto num_threads = 10;
|
||||
auto locals = py::dict("count"_a=0);
|
||||
auto locals = py::dict("count"_a = 0);
|
||||
|
||||
{
|
||||
py::gil_scoped_release gil_release{};
|
||||
@ -322,9 +328,8 @@ TEST_CASE("Reload module from file") {
|
||||
bool dont_write_bytecode = sys.attr("dont_write_bytecode").cast<bool>();
|
||||
sys.attr("dont_write_bytecode") = true;
|
||||
// Reset the value at scope exit
|
||||
scope_exit reset_dont_write_bytecode([&]() {
|
||||
sys.attr("dont_write_bytecode") = dont_write_bytecode;
|
||||
});
|
||||
scope_exit reset_dont_write_bytecode(
|
||||
[&]() { sys.attr("dont_write_bytecode") = dont_write_bytecode; });
|
||||
|
||||
std::string module_name = "test_module_reload";
|
||||
std::string module_file = module_name + ".py";
|
||||
@ -335,9 +340,7 @@ TEST_CASE("Reload module from file") {
|
||||
test_module << " return 1\n";
|
||||
test_module.close();
|
||||
// Delete the file at scope exit
|
||||
scope_exit delete_module_file([&]() {
|
||||
std::remove(module_file.c_str());
|
||||
});
|
||||
scope_exit delete_module_file([&]() { std::remove(module_file.c_str()); });
|
||||
|
||||
// Import the module from file
|
||||
auto module_ = py::module_::import(module_name.c_str());
|
||||
|
||||
@ -11,11 +11,7 @@
|
||||
|
||||
TEST_SUBMODULE(enums, m) {
|
||||
// test_unscoped_enum
|
||||
enum UnscopedEnum {
|
||||
EOne = 1,
|
||||
ETwo,
|
||||
EThree
|
||||
};
|
||||
enum UnscopedEnum { EOne = 1, ETwo, EThree };
|
||||
py::enum_<UnscopedEnum>(m, "UnscopedEnum", py::arithmetic(), "An unscoped enumeration")
|
||||
.value("EOne", EOne, "Docstring for EOne")
|
||||
.value("ETwo", ETwo, "Docstring for ETwo")
|
||||
@ -23,10 +19,7 @@ TEST_SUBMODULE(enums, m) {
|
||||
.export_values();
|
||||
|
||||
// test_scoped_enum
|
||||
enum class ScopedEnum {
|
||||
Two = 2,
|
||||
Three
|
||||
};
|
||||
enum class ScopedEnum { Two = 2, Three };
|
||||
py::enum_<ScopedEnum>(m, "ScopedEnum", py::arithmetic())
|
||||
.value("Two", ScopedEnum::Two)
|
||||
.value("Three", ScopedEnum::Three);
|
||||
@ -36,11 +29,7 @@ TEST_SUBMODULE(enums, m) {
|
||||
});
|
||||
|
||||
// test_binary_operators
|
||||
enum Flags {
|
||||
Read = 4,
|
||||
Write = 2,
|
||||
Execute = 1
|
||||
};
|
||||
enum Flags { Read = 4, Write = 2, Execute = 1 };
|
||||
py::enum_<Flags>(m, "Flags", py::arithmetic())
|
||||
.value("Read", Flags::Read)
|
||||
.value("Write", Flags::Write)
|
||||
@ -50,14 +39,9 @@ TEST_SUBMODULE(enums, m) {
|
||||
// test_implicit_conversion
|
||||
class ClassWithUnscopedEnum {
|
||||
public:
|
||||
enum EMode {
|
||||
EFirstMode = 1,
|
||||
ESecondMode
|
||||
};
|
||||
enum EMode { EFirstMode = 1, ESecondMode };
|
||||
|
||||
static EMode test_function(EMode mode) {
|
||||
return mode;
|
||||
}
|
||||
static EMode test_function(EMode mode) { return mode; }
|
||||
};
|
||||
py::class_<ClassWithUnscopedEnum> exenum_class(m, "ClassWithUnscopedEnum");
|
||||
exenum_class.def_static("test_function", &ClassWithUnscopedEnum::test_function);
|
||||
@ -67,19 +51,17 @@ TEST_SUBMODULE(enums, m) {
|
||||
.export_values();
|
||||
|
||||
// test_enum_to_int
|
||||
m.def("test_enum_to_int", [](int) { });
|
||||
m.def("test_enum_to_uint", [](uint32_t) { });
|
||||
m.def("test_enum_to_long_long", [](long long) { });
|
||||
m.def("test_enum_to_int", [](int) {});
|
||||
m.def("test_enum_to_uint", [](uint32_t) {});
|
||||
m.def("test_enum_to_long_long", [](long long) {});
|
||||
|
||||
// test_duplicate_enum_name
|
||||
enum SimpleEnum
|
||||
{
|
||||
ONE, TWO, THREE
|
||||
};
|
||||
enum SimpleEnum { ONE, TWO, THREE };
|
||||
|
||||
m.def("register_bad_enum", [m]() {
|
||||
py::enum_<SimpleEnum>(m, "SimpleEnum")
|
||||
.value("ONE", SimpleEnum::ONE) //NOTE: all value function calls are called with the same first parameter value
|
||||
.value("ONE", SimpleEnum::ONE) // NOTE: all value function calls are called with the
|
||||
// same first parameter value
|
||||
.value("ONE", SimpleEnum::TWO)
|
||||
.value("ONE", SimpleEnum::THREE)
|
||||
.export_values();
|
||||
@ -90,33 +72,36 @@ TEST_SUBMODULE(enums, m) {
|
||||
enum class ScopedShortEnum : short {};
|
||||
enum class ScopedLongEnum : long {};
|
||||
enum UnscopedUInt64Enum : std::uint64_t {};
|
||||
static_assert(py::detail::all_of<
|
||||
std::is_same<py::enum_<UnscopedUCharEnum>::Scalar, unsigned char>,
|
||||
std::is_same<py::enum_<ScopedShortEnum>::Scalar, short>,
|
||||
std::is_same<py::enum_<ScopedLongEnum>::Scalar, long>,
|
||||
std::is_same<py::enum_<UnscopedUInt64Enum>::Scalar, std::uint64_t>
|
||||
>::value, "Error during the deduction of enum's scalar type with normal integer underlying");
|
||||
static_assert(
|
||||
py::detail::all_of<
|
||||
std::is_same<py::enum_<UnscopedUCharEnum>::Scalar, unsigned char>,
|
||||
std::is_same<py::enum_<ScopedShortEnum>::Scalar, short>,
|
||||
std::is_same<py::enum_<ScopedLongEnum>::Scalar, long>,
|
||||
std::is_same<py::enum_<UnscopedUInt64Enum>::Scalar, std::uint64_t>>::value,
|
||||
"Error during the deduction of enum's scalar type with normal integer underlying");
|
||||
|
||||
// test_enum_scalar_with_char_underlying
|
||||
enum class ScopedCharEnum : char { Zero, Positive };
|
||||
enum class ScopedWCharEnum : wchar_t { Zero, Positive };
|
||||
enum class ScopedCharEnum : char { Zero, Positive };
|
||||
enum class ScopedWCharEnum : wchar_t { Zero, Positive };
|
||||
enum class ScopedChar32Enum : char32_t { Zero, Positive };
|
||||
enum class ScopedChar16Enum : char16_t { Zero, Positive };
|
||||
|
||||
// test the scalar of char type enums according to chapter 'Character types'
|
||||
// from https://en.cppreference.com/w/cpp/language/types
|
||||
static_assert(py::detail::any_of<
|
||||
std::is_same<py::enum_<ScopedCharEnum>::Scalar, signed char>, // e.g. gcc on x86
|
||||
std::is_same<py::enum_<ScopedCharEnum>::Scalar, unsigned char> // e.g. arm linux
|
||||
>::value, "char should be cast to either signed char or unsigned char");
|
||||
static_assert(
|
||||
sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 2 ||
|
||||
sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 4
|
||||
, "wchar_t should be either 16 bits (Windows) or 32 (everywhere else)");
|
||||
static_assert(py::detail::all_of<
|
||||
std::is_same<py::enum_<ScopedChar32Enum>::Scalar, std::uint_least32_t>,
|
||||
std::is_same<py::enum_<ScopedChar16Enum>::Scalar, std::uint_least16_t>
|
||||
>::value, "char32_t, char16_t (and char8_t)'s size, signedness, and alignment is determined");
|
||||
py::detail::any_of<
|
||||
std::is_same<py::enum_<ScopedCharEnum>::Scalar, signed char>, // e.g. gcc on x86
|
||||
std::is_same<py::enum_<ScopedCharEnum>::Scalar, unsigned char> // e.g. arm linux
|
||||
>::value,
|
||||
"char should be cast to either signed char or unsigned char");
|
||||
static_assert(sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 2
|
||||
|| sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 4,
|
||||
"wchar_t should be either 16 bits (Windows) or 32 (everywhere else)");
|
||||
static_assert(
|
||||
py::detail::all_of<
|
||||
std::is_same<py::enum_<ScopedChar32Enum>::Scalar, std::uint_least32_t>,
|
||||
std::is_same<py::enum_<ScopedChar16Enum>::Scalar, std::uint_least16_t>>::value,
|
||||
"char32_t, char16_t (and char8_t)'s size, signedness, and alignment is determined");
|
||||
#if defined(PYBIND11_HAS_U8STRING)
|
||||
enum class ScopedChar8Enum : char8_t { Zero, Positive };
|
||||
static_assert(std::is_same<py::enum_<ScopedChar8Enum>::Scalar, unsigned char>::value);
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
|
||||
#include <pybind11/eval.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
TEST_SUBMODULE(eval_, m) {
|
||||
@ -20,16 +20,13 @@ TEST_SUBMODULE(eval_, m) {
|
||||
|
||||
m.def("test_eval_statements", [global]() {
|
||||
auto local = py::dict();
|
||||
local["call_test"] = py::cpp_function([&]() -> int {
|
||||
return 42;
|
||||
});
|
||||
local["call_test"] = py::cpp_function([&]() -> int { return 42; });
|
||||
|
||||
// Regular string literal
|
||||
py::exec(
|
||||
"message = 'Hello World!'\n"
|
||||
"x = call_test()",
|
||||
global, local
|
||||
);
|
||||
py::exec("message = 'Hello World!'\n"
|
||||
"x = call_test()",
|
||||
global,
|
||||
local);
|
||||
|
||||
// Multi-line raw string literal
|
||||
py::exec(R"(
|
||||
@ -37,8 +34,9 @@ TEST_SUBMODULE(eval_, m) {
|
||||
print(message)
|
||||
else:
|
||||
raise RuntimeError
|
||||
)", global, local
|
||||
);
|
||||
)",
|
||||
global,
|
||||
local);
|
||||
auto x = local["x"].cast<int>();
|
||||
|
||||
return x == 42;
|
||||
@ -53,9 +51,7 @@ TEST_SUBMODULE(eval_, m) {
|
||||
|
||||
m.def("test_eval_single_statement", []() {
|
||||
auto local = py::dict();
|
||||
local["call_test"] = py::cpp_function([&]() -> int {
|
||||
return 42;
|
||||
});
|
||||
local["call_test"] = py::cpp_function([&]() -> int { return 42; });
|
||||
|
||||
auto result = py::eval<py::eval_single_statement>("x = call_test()", py::dict(), local);
|
||||
auto x = local["x"].cast<int>();
|
||||
@ -114,7 +110,9 @@ TEST_SUBMODULE(eval_, m) {
|
||||
|
||||
def func_local():
|
||||
return local_value
|
||||
)", global, local);
|
||||
)",
|
||||
global,
|
||||
local);
|
||||
return std::make_pair(global, local);
|
||||
});
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
#include "test_exceptions.h"
|
||||
|
||||
#include "local_bindings.h"
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
@ -18,8 +18,9 @@
|
||||
// A type that should be raised as an exception in Python
|
||||
class MyException : public std::exception {
|
||||
public:
|
||||
explicit MyException(const char * m) : message{m} {}
|
||||
const char * what() const noexcept override {return message.c_str();}
|
||||
explicit MyException(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
@ -27,8 +28,9 @@ private:
|
||||
// A type that should be translated to a standard Python exception
|
||||
class MyException2 : public std::exception {
|
||||
public:
|
||||
explicit MyException2(const char * m) : message{m} {}
|
||||
const char * what() const noexcept override {return message.c_str();}
|
||||
explicit MyException2(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
@ -36,13 +38,13 @@ private:
|
||||
// A type that is not derived from std::exception (and is thus unknown)
|
||||
class MyException3 {
|
||||
public:
|
||||
explicit MyException3(const char * m) : message{m} {}
|
||||
virtual const char * what() const noexcept {return message.c_str();}
|
||||
explicit MyException3(const char *m) : message{m} {}
|
||||
virtual const char *what() const noexcept { return message.c_str(); }
|
||||
// Rule of 5 BEGIN: to preempt compiler warnings.
|
||||
MyException3(const MyException3&) = default;
|
||||
MyException3(MyException3&&) = default;
|
||||
MyException3& operator=(const MyException3&) = default;
|
||||
MyException3& operator=(MyException3&&) = default;
|
||||
MyException3(const MyException3 &) = default;
|
||||
MyException3(MyException3 &&) = default;
|
||||
MyException3 &operator=(const MyException3 &) = default;
|
||||
MyException3 &operator=(MyException3 &&) = default;
|
||||
virtual ~MyException3() = default;
|
||||
// Rule of 5 END.
|
||||
private:
|
||||
@ -53,13 +55,13 @@ private:
|
||||
// and delegated to its exception translator
|
||||
class MyException4 : public std::exception {
|
||||
public:
|
||||
explicit MyException4(const char * m) : message{m} {}
|
||||
const char * what() const noexcept override {return message.c_str();}
|
||||
explicit MyException4(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
|
||||
// Like the above, but declared via the helper function
|
||||
class MyException5 : public std::logic_error {
|
||||
public:
|
||||
@ -71,17 +73,16 @@ class MyException5_1 : public MyException5 {
|
||||
using MyException5::MyException5;
|
||||
};
|
||||
|
||||
|
||||
// Exception that will be caught via the module local translator.
|
||||
class MyException6 : public std::exception {
|
||||
public:
|
||||
explicit MyException6(const char * m) : message{m} {}
|
||||
const char * what() const noexcept override {return message.c_str();}
|
||||
explicit MyException6(const char *m) : message{m} {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message = "";
|
||||
};
|
||||
|
||||
|
||||
struct PythonCallInDestructor {
|
||||
explicit PythonCallInDestructor(const py::dict &d) : d(d) {}
|
||||
~PythonCallInDestructor() { d["good"] = true; }
|
||||
@ -89,8 +90,6 @@ struct PythonCallInDestructor {
|
||||
py::dict d;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct PythonAlreadySetInDestructor {
|
||||
explicit PythonAlreadySetInDestructor(const py::str &s) : s(s) {}
|
||||
~PythonAlreadySetInDestructor() {
|
||||
@ -98,8 +97,7 @@ struct PythonAlreadySetInDestructor {
|
||||
try {
|
||||
// Assign to a py::object to force read access of nonexistent dict entry
|
||||
py::object o = foo["bar"];
|
||||
}
|
||||
catch (py::error_already_set& ex) {
|
||||
} catch (py::error_already_set &ex) {
|
||||
ex.discard_as_unraisable(s);
|
||||
}
|
||||
}
|
||||
@ -108,9 +106,8 @@ struct PythonAlreadySetInDestructor {
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(exceptions, m) {
|
||||
m.def("throw_std_exception", []() {
|
||||
throw std::runtime_error("This exception was intentionally thrown.");
|
||||
});
|
||||
m.def("throw_std_exception",
|
||||
[]() { throw std::runtime_error("This exception was intentionally thrown."); });
|
||||
|
||||
// make a new custom exception and use it as a translation target
|
||||
static py::exception<MyException> ex(m, "MyException");
|
||||
@ -157,26 +154,30 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
// A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
|
||||
py::register_exception<MyException5_1>(m, "MyException5_1", ex5.ptr());
|
||||
|
||||
//py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException")
|
||||
// py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException")
|
||||
|
||||
py::register_local_exception_translator([](std::exception_ptr p) {
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const MyException6 &e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
}
|
||||
try {
|
||||
if (p) {
|
||||
std::rethrow_exception(p);
|
||||
}
|
||||
} catch (const MyException6 &e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
}
|
||||
});
|
||||
|
||||
m.def("throws1", []() { throw MyException("this error should go to a custom type"); });
|
||||
m.def("throws2", []() { throw MyException2("this error should go to a standard Python exception"); });
|
||||
m.def("throws2",
|
||||
[]() { throw MyException2("this error should go to a standard Python exception"); });
|
||||
m.def("throws3", []() { throw MyException3("this error cannot be translated"); });
|
||||
m.def("throws4", []() { throw MyException4("this error is rethrown"); });
|
||||
m.def("throws5", []() { throw MyException5("this is a helper-defined translated exception"); });
|
||||
m.def("throws5",
|
||||
[]() { throw MyException5("this is a helper-defined translated exception"); });
|
||||
m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); });
|
||||
m.def("throws6", []() { throw MyException6("MyException6 only handled in this module"); });
|
||||
m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); });
|
||||
m.def("throws_logic_error", []() {
|
||||
throw std::logic_error("this error should fall through to the standard handler");
|
||||
});
|
||||
m.def("throws_overflow_error", []() { throw std::overflow_error(""); });
|
||||
m.def("throws_local_error", []() { throw LocalException("never caught"); });
|
||||
m.def("throws_local_simple_error", []() { throw LocalSimpleException("this mod"); });
|
||||
@ -185,8 +186,7 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
try {
|
||||
// Assign to a py::object to force read access of nonexistent dict entry
|
||||
py::object o = foo["bar"];
|
||||
}
|
||||
catch (py::error_already_set& ex) {
|
||||
} catch (py::error_already_set &ex) {
|
||||
if (!ex.matches(PyExc_KeyError)) {
|
||||
throw;
|
||||
}
|
||||
@ -199,8 +199,7 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
try {
|
||||
// Assign to a py::object to force read access of nonexistent dict entry
|
||||
py::object o = foo["bar"];
|
||||
}
|
||||
catch (py::error_already_set &ex) {
|
||||
} catch (py::error_already_set &ex) {
|
||||
if (!ex.matches(PyExc_Exception)) {
|
||||
throw;
|
||||
}
|
||||
@ -212,8 +211,7 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
try {
|
||||
// On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
|
||||
py::module_::import("nonexistent");
|
||||
}
|
||||
catch (py::error_already_set &ex) {
|
||||
} catch (py::error_already_set &ex) {
|
||||
if (!ex.matches(PyExc_ImportError)) {
|
||||
throw;
|
||||
}
|
||||
@ -228,10 +226,9 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
}
|
||||
try {
|
||||
throw py::error_already_set();
|
||||
} catch (const std::runtime_error& e) {
|
||||
if ((err && e.what() != std::string("ValueError: foo")) ||
|
||||
(!err && e.what() != std::string("Unknown internal error occurred")))
|
||||
{
|
||||
} catch (const std::runtime_error &e) {
|
||||
if ((err && e.what() != std::string("ValueError: foo"))
|
||||
|| (!err && e.what() != std::string("Unknown internal error occurred"))) {
|
||||
PyErr_Clear();
|
||||
throw std::runtime_error("error message mismatch");
|
||||
}
|
||||
@ -249,7 +246,7 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
PythonCallInDestructor set_dict_in_destructor(d);
|
||||
PyErr_SetString(PyExc_ValueError, "foo");
|
||||
throw py::error_already_set();
|
||||
} catch (const py::error_already_set&) {
|
||||
} catch (const py::error_already_set &) {
|
||||
retval = true;
|
||||
}
|
||||
return retval;
|
||||
@ -275,7 +272,7 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
});
|
||||
|
||||
// Test repr that cannot be displayed
|
||||
m.def("simple_bool_passthrough", [](bool x) {return x;});
|
||||
m.def("simple_bool_passthrough", [](bool x) { return x; });
|
||||
|
||||
m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); });
|
||||
|
||||
@ -291,7 +288,7 @@ TEST_SUBMODULE(exceptions, m) {
|
||||
try {
|
||||
PyErr_SetString(PyExc_ValueError, "inner");
|
||||
throw py::error_already_set();
|
||||
} catch (py::error_already_set& e) {
|
||||
} catch (py::error_already_set &e) {
|
||||
py::raise_from(e, PyExc_ValueError, "outer");
|
||||
throw py::error_already_set();
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
// shared exceptions for cross_module_tests
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
@ -87,6 +88,7 @@ class TestFactory6 {
|
||||
protected:
|
||||
int value;
|
||||
bool alias = false;
|
||||
|
||||
public:
|
||||
explicit TestFactory6(int i) : value{i} { print_created(this, i); }
|
||||
TestFactory6(TestFactory6 &&f) noexcept {
|
||||
@ -133,6 +135,7 @@ class TestFactory7 {
|
||||
protected:
|
||||
int value;
|
||||
bool alias = false;
|
||||
|
||||
public:
|
||||
explicit TestFactory7(int i) : value{i} { print_created(this, i); }
|
||||
TestFactory7(TestFactory7 &&f) noexcept {
|
||||
@ -166,14 +169,15 @@ public:
|
||||
int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); }
|
||||
};
|
||||
|
||||
|
||||
class TestFactoryHelper {
|
||||
public:
|
||||
// Non-movable, non-copyable type:
|
||||
// Return via pointer:
|
||||
static TestFactory1 *construct1() { return new TestFactory1(); }
|
||||
// Holder:
|
||||
static std::unique_ptr<TestFactory1> construct1(int a) { return std::unique_ptr<TestFactory1>(new TestFactory1(a)); }
|
||||
static std::unique_ptr<TestFactory1> construct1(int a) {
|
||||
return std::unique_ptr<TestFactory1>(new TestFactory1(a));
|
||||
}
|
||||
// pointer again
|
||||
static TestFactory1 *construct1_string(std::string a) {
|
||||
return new TestFactory1(std::move(a));
|
||||
@ -183,7 +187,9 @@ public:
|
||||
// pointer:
|
||||
static TestFactory2 *construct2() { return new TestFactory2(); }
|
||||
// holder:
|
||||
static std::unique_ptr<TestFactory2> construct2(int a) { return std::unique_ptr<TestFactory2>(new TestFactory2(a)); }
|
||||
static std::unique_ptr<TestFactory2> construct2(int a) {
|
||||
return std::unique_ptr<TestFactory2>(new TestFactory2(a));
|
||||
}
|
||||
// by value moving:
|
||||
static TestFactory2 construct2(std::string a) { return TestFactory2(std::move(a)); }
|
||||
|
||||
@ -191,16 +197,18 @@ public:
|
||||
// pointer:
|
||||
static TestFactory3 *construct3() { return new TestFactory3(); }
|
||||
// holder:
|
||||
static std::shared_ptr<TestFactory3> construct3(int a) { return std::shared_ptr<TestFactory3>(new TestFactory3(a)); }
|
||||
static std::shared_ptr<TestFactory3> construct3(int a) {
|
||||
return std::shared_ptr<TestFactory3>(new TestFactory3(a));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(factory_constructors, m) {
|
||||
|
||||
// Define various trivial types to allow simpler overload resolution:
|
||||
py::module_ m_tag = m.def_submodule("tag");
|
||||
#define MAKE_TAG_TYPE(Name) \
|
||||
struct Name##_tag {}; \
|
||||
py::class_<Name##_tag>(m_tag, #Name "_tag").def(py::init<>()); \
|
||||
#define MAKE_TAG_TYPE(Name) \
|
||||
struct Name##_tag {}; \
|
||||
py::class_<Name##_tag>(m_tag, #Name "_tag").def(py::init<>()); \
|
||||
m_tag.attr(#Name) = py::cast(Name##_tag{})
|
||||
MAKE_TAG_TYPE(pointer);
|
||||
MAKE_TAG_TYPE(unique_ptr);
|
||||
@ -223,9 +231,9 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
.def(py::init([](unique_ptr_tag, int v) { return TestFactoryHelper::construct1(v); }))
|
||||
.def(py::init(&TestFactoryHelper::construct1_string)) // raw function pointer
|
||||
.def(py::init([](pointer_tag) { return TestFactoryHelper::construct1(); }))
|
||||
.def(py::init([](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); }))
|
||||
.def_readwrite("value", &TestFactory1::value)
|
||||
;
|
||||
.def(py::init(
|
||||
[](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); }))
|
||||
.def_readwrite("value", &TestFactory1::value);
|
||||
py::class_<TestFactory2>(m, "TestFactory2")
|
||||
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); }))
|
||||
.def(py::init([](unique_ptr_tag, std::string v) {
|
||||
@ -236,7 +244,10 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
|
||||
// Stateful & reused:
|
||||
int c = 1;
|
||||
auto c4a = [c](pointer_tag, TF4_tag, int a) { (void) c; return new TestFactory4(a);};
|
||||
auto c4a = [c](pointer_tag, TF4_tag, int a) {
|
||||
(void) c;
|
||||
return new TestFactory4(a);
|
||||
};
|
||||
|
||||
// test_init_factory_basic, test_init_factory_casting
|
||||
py::class_<TestFactory3, std::shared_ptr<TestFactory3>> pyTestFactory3(m, "TestFactory3");
|
||||
@ -253,16 +264,17 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
.def(py::init(c4a)) // derived ptr
|
||||
.def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); }))
|
||||
// derived shared ptr:
|
||||
.def(py::init([](shared_ptr_tag, TF4_tag, int a) { return std::make_shared<TestFactory4>(a); }))
|
||||
.def(py::init([](shared_ptr_tag, TF5_tag, int a) { return std::make_shared<TestFactory5>(a); }))
|
||||
.def(py::init(
|
||||
[](shared_ptr_tag, TF4_tag, int a) { return std::make_shared<TestFactory4>(a); }))
|
||||
.def(py::init(
|
||||
[](shared_ptr_tag, TF5_tag, int a) { return std::make_shared<TestFactory5>(a); }))
|
||||
|
||||
// Returns nullptr:
|
||||
.def(py::init([](null_ptr_tag) { return (TestFactory3 *) nullptr; }))
|
||||
.def(py::init([](null_unique_ptr_tag) { return std::unique_ptr<TestFactory3>(); }))
|
||||
.def(py::init([](null_shared_ptr_tag) { return std::shared_ptr<TestFactory3>(); }))
|
||||
|
||||
.def_readwrite("value", &TestFactory3::value)
|
||||
;
|
||||
.def_readwrite("value", &TestFactory3::value);
|
||||
|
||||
// test_init_factory_casting
|
||||
py::class_<TestFactory4, TestFactory3, std::shared_ptr<TestFactory4>>(m, "TestFactory4")
|
||||
@ -346,9 +358,7 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
py::class_<NoPlacementNew>(m, "NoPlacementNew")
|
||||
.def(py::init<int>())
|
||||
.def(py::init([]() { return new NoPlacementNew(100); }))
|
||||
.def_readwrite("i", &NoPlacementNew::i)
|
||||
;
|
||||
|
||||
.def_readwrite("i", &NoPlacementNew::i);
|
||||
|
||||
// test_reallocations
|
||||
// Class that has verbose operator_new/operator_delete calls
|
||||
@ -358,23 +368,36 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
explicit NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); }
|
||||
~NoisyAlloc() { py::print("~NoisyAlloc()"); }
|
||||
|
||||
static void *operator new(size_t s) { py::print("noisy new"); return ::operator new(s); }
|
||||
static void *operator new(size_t, void *p) { py::print("noisy placement new"); return p; }
|
||||
static void operator delete(void *p, size_t) { py::print("noisy delete"); ::operator delete(p); }
|
||||
static void *operator new(size_t s) {
|
||||
py::print("noisy new");
|
||||
return ::operator new(s);
|
||||
}
|
||||
static void *operator new(size_t, void *p) {
|
||||
py::print("noisy placement new");
|
||||
return p;
|
||||
}
|
||||
static void operator delete(void *p, size_t) {
|
||||
py::print("noisy delete");
|
||||
::operator delete(p);
|
||||
}
|
||||
static void operator delete(void *, void *) { py::print("noisy placement delete"); }
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
// MSVC 2015 bug: the above "noisy delete" isn't invoked (fixed in MSVC 2017)
|
||||
static void operator delete(void *p) { py::print("noisy delete"); ::operator delete(p); }
|
||||
static void operator delete(void *p) {
|
||||
py::print("noisy delete");
|
||||
::operator delete(p);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
py::class_<NoisyAlloc> pyNoisyAlloc(m, "NoisyAlloc");
|
||||
// Since these overloads have the same number of arguments, the dispatcher will try each of
|
||||
// them until the arguments convert. Thus we can get a pre-allocation here when passing a
|
||||
// single non-integer:
|
||||
// Since these overloads have the same number of arguments, the dispatcher will try each of
|
||||
// them until the arguments convert. Thus we can get a pre-allocation here when passing a
|
||||
// single non-integer:
|
||||
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
|
||||
pyNoisyAlloc.def("__init__", [](NoisyAlloc *a, int i) { new (a) NoisyAlloc(i); }); // Regular constructor, runs first, requires preallocation
|
||||
pyNoisyAlloc.def("__init__", [](NoisyAlloc *a, int i) {
|
||||
new (a) NoisyAlloc(i);
|
||||
}); // Regular constructor, runs first, requires preallocation
|
||||
});
|
||||
|
||||
pyNoisyAlloc.def(py::init([](double d) { return new NoisyAlloc(d); }));
|
||||
@ -385,7 +408,8 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
pyNoisyAlloc.def(py::init([](double d, int) { return NoisyAlloc(d); }));
|
||||
// Old-style placement new init; requires preallocation
|
||||
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() {
|
||||
pyNoisyAlloc.def("__init__", [](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); });
|
||||
pyNoisyAlloc.def("__init__",
|
||||
[](NoisyAlloc &a, double d, double) { new (&a) NoisyAlloc(d); });
|
||||
});
|
||||
// Requires deallocation of previous overload preallocated value:
|
||||
pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); }));
|
||||
@ -395,7 +419,8 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
"__init__", [](NoisyAlloc &a, int i, const std::string &) { new (&a) NoisyAlloc(i); });
|
||||
});
|
||||
|
||||
// static_assert testing (the following def's should all fail with appropriate compilation errors):
|
||||
// static_assert testing (the following def's should all fail with appropriate compilation
|
||||
// errors):
|
||||
#if 0
|
||||
struct BadF1Base {};
|
||||
struct BadF1 : BadF1Base {};
|
||||
|
||||
@ -7,43 +7,41 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
class VirtClass {
|
||||
class VirtClass {
|
||||
public:
|
||||
virtual ~VirtClass() = default;
|
||||
VirtClass() = default;
|
||||
VirtClass(const VirtClass&) = delete;
|
||||
VirtClass(const VirtClass &) = delete;
|
||||
virtual void virtual_func() {}
|
||||
virtual void pure_virtual_func() = 0;
|
||||
};
|
||||
|
||||
class PyVirtClass : public VirtClass {
|
||||
void virtual_func() override {
|
||||
PYBIND11_OVERRIDE(void, VirtClass, virtual_func,);
|
||||
}
|
||||
void virtual_func() override { PYBIND11_OVERRIDE(void, VirtClass, virtual_func, ); }
|
||||
void pure_virtual_func() override {
|
||||
PYBIND11_OVERRIDE_PURE(void, VirtClass, pure_virtual_func,);
|
||||
PYBIND11_OVERRIDE_PURE(void, VirtClass, pure_virtual_func, );
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(gil_scoped, m) {
|
||||
py::class_<VirtClass, PyVirtClass>(m, "VirtClass")
|
||||
.def(py::init<>())
|
||||
.def("virtual_func", &VirtClass::virtual_func)
|
||||
.def("pure_virtual_func", &VirtClass::pure_virtual_func);
|
||||
py::class_<VirtClass, PyVirtClass>(m, "VirtClass")
|
||||
.def(py::init<>())
|
||||
.def("virtual_func", &VirtClass::virtual_func)
|
||||
.def("pure_virtual_func", &VirtClass::pure_virtual_func);
|
||||
|
||||
m.def("test_callback_py_obj", [](py::object &func) { func(); });
|
||||
m.def("test_callback_std_func", [](const std::function<void()> &func) { func(); });
|
||||
m.def("test_callback_virtual_func", [](VirtClass &virt) { virt.virtual_func(); });
|
||||
m.def("test_callback_pure_virtual_func", [](VirtClass &virt) { virt.pure_virtual_func(); });
|
||||
m.def("test_cross_module_gil", []() {
|
||||
auto cm = py::module_::import("cross_module_gil_utils");
|
||||
auto gil_acquire
|
||||
= reinterpret_cast<void (*)()>(PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr()));
|
||||
py::gil_scoped_release gil_release;
|
||||
gil_acquire();
|
||||
});
|
||||
m.def("test_callback_py_obj", [](py::object &func) { func(); });
|
||||
m.def("test_callback_std_func", [](const std::function<void()> &func) { func(); });
|
||||
m.def("test_callback_virtual_func", [](VirtClass &virt) { virt.virtual_func(); });
|
||||
m.def("test_callback_pure_virtual_func", [](VirtClass &virt) { virt.pure_virtual_func(); });
|
||||
m.def("test_cross_module_gil", []() {
|
||||
auto cm = py::module_::import("cross_module_gil_utils");
|
||||
auto gil_acquire = reinterpret_cast<void (*)()>(
|
||||
PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr()));
|
||||
py::gil_scoped_release gil_release;
|
||||
gil_acquire();
|
||||
});
|
||||
}
|
||||
|
||||
@ -7,12 +7,14 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC
|
||||
# pragma warning(disable: 4702) // unreachable code in system header (xatomic.h(382))
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC
|
||||
# pragma warning(disable : 4702) // unreachable code in system header (xatomic.h(382))
|
||||
#endif
|
||||
|
||||
#include <pybind11/iostream.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
@ -51,13 +53,12 @@ struct TestThread {
|
||||
std::cout << "x" << std::flush;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(50));
|
||||
} };
|
||||
}
|
||||
};
|
||||
t_ = new std::thread(std::move(thread_f));
|
||||
}
|
||||
|
||||
~TestThread() {
|
||||
delete t_;
|
||||
}
|
||||
~TestThread() { delete t_; }
|
||||
|
||||
void stop() { stop_ = true; }
|
||||
|
||||
@ -75,7 +76,6 @@ struct TestThread {
|
||||
std::atomic<bool> stop_;
|
||||
};
|
||||
|
||||
|
||||
TEST_SUBMODULE(iostream, m) {
|
||||
|
||||
add_ostream_redirect(m);
|
||||
@ -92,9 +92,11 @@ TEST_SUBMODULE(iostream, m) {
|
||||
std::cout << msg << std::flush;
|
||||
});
|
||||
|
||||
m.def("guard_output", &noisy_function,
|
||||
py::call_guard<py::scoped_ostream_redirect>(),
|
||||
py::arg("msg"), py::arg("flush")=true);
|
||||
m.def("guard_output",
|
||||
&noisy_function,
|
||||
py::call_guard<py::scoped_ostream_redirect>(),
|
||||
py::arg("msg"),
|
||||
py::arg("flush") = true);
|
||||
|
||||
m.def("captured_err", [](const std::string &msg) {
|
||||
py::scoped_ostream_redirect redir(std::cerr, py::module_::import("sys").attr("stderr"));
|
||||
@ -103,9 +105,11 @@ TEST_SUBMODULE(iostream, m) {
|
||||
|
||||
m.def("noisy_function", &noisy_function, py::arg("msg"), py::arg("flush") = true);
|
||||
|
||||
m.def("dual_guard", &noisy_funct_dual,
|
||||
py::call_guard<py::scoped_ostream_redirect, py::scoped_estream_redirect>(),
|
||||
py::arg("msg"), py::arg("emsg"));
|
||||
m.def("dual_guard",
|
||||
&noisy_funct_dual,
|
||||
py::call_guard<py::scoped_ostream_redirect, py::scoped_estream_redirect>(),
|
||||
py::arg("msg"),
|
||||
py::arg("emsg"));
|
||||
|
||||
m.def("raw_output", [](const std::string &msg) { std::cout << msg << std::flush; });
|
||||
|
||||
|
||||
@ -7,39 +7,43 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
auto kw_func = [](int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); };
|
||||
auto kw_func
|
||||
= [](int x, int y) { return "x=" + std::to_string(x) + ", y=" + std::to_string(y); };
|
||||
|
||||
// test_named_arguments
|
||||
m.def("kw_func0", kw_func);
|
||||
m.def("kw_func1", kw_func, py::arg("x"), py::arg("y"));
|
||||
m.def("kw_func2", kw_func, py::arg("x") = 100, py::arg("y") = 200);
|
||||
m.def("kw_func3", [](const char *) { }, py::arg("data") = std::string("Hello world!"));
|
||||
m.def(
|
||||
"kw_func3", [](const char *) {}, py::arg("data") = std::string("Hello world!"));
|
||||
|
||||
/* A fancier default argument */
|
||||
std::vector<int> list{{13, 17}};
|
||||
m.def("kw_func4", [](const std::vector<int> &entries) {
|
||||
std::string ret = "{";
|
||||
for (int i : entries) {
|
||||
ret += std::to_string(i) + " ";
|
||||
}
|
||||
ret.back() = '}';
|
||||
return ret;
|
||||
}, py::arg("myList") = list);
|
||||
m.def(
|
||||
"kw_func4",
|
||||
[](const std::vector<int> &entries) {
|
||||
std::string ret = "{";
|
||||
for (int i : entries) {
|
||||
ret += std::to_string(i) + " ";
|
||||
}
|
||||
ret.back() = '}';
|
||||
return ret;
|
||||
},
|
||||
py::arg("myList") = list);
|
||||
|
||||
m.def("kw_func_udl", kw_func, "x"_a, "y"_a=300);
|
||||
m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a=0);
|
||||
m.def("kw_func_udl", kw_func, "x"_a, "y"_a = 300);
|
||||
m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a = 0);
|
||||
|
||||
// test_args_and_kwargs
|
||||
m.def("args_function", [](py::args args) -> py::tuple {
|
||||
return std::move(args);
|
||||
});
|
||||
m.def("args_function", [](py::args args) -> py::tuple { return std::move(args); });
|
||||
m.def("args_kwargs_function", [](const py::args &args, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(args, kwargs);
|
||||
});
|
||||
@ -54,35 +58,60 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
};
|
||||
m.def("mixed_plus_args_kwargs", mixed_plus_both);
|
||||
|
||||
m.def("mixed_plus_args_kwargs_defaults", mixed_plus_both,
|
||||
py::arg("i") = 1, py::arg("j") = 3.14159);
|
||||
m.def("mixed_plus_args_kwargs_defaults",
|
||||
mixed_plus_both,
|
||||
py::arg("i") = 1,
|
||||
py::arg("j") = 3.14159);
|
||||
|
||||
m.def("args_kwonly",
|
||||
[](int i, double j, const py::args &args, int z) { return py::make_tuple(i, j, args, z); },
|
||||
"i"_a, "j"_a, "z"_a);
|
||||
m.def("args_kwonly_kwargs",
|
||||
[](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(i, j, args, z, kwargs); },
|
||||
"i"_a, "j"_a, py::kw_only{}, "z"_a);
|
||||
m.def("args_kwonly_kwargs_defaults",
|
||||
[](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(i, j, args, z, kwargs); },
|
||||
"i"_a = 1, "j"_a = 3.14159, "z"_a = 42);
|
||||
m.def("args_kwonly_full_monty",
|
||||
[](int h, int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(h, i, j, args, z, kwargs); },
|
||||
py::arg() = 1, py::arg() = 2, py::pos_only{}, "j"_a = 3.14159, "z"_a = 42);
|
||||
m.def(
|
||||
"args_kwonly",
|
||||
[](int i, double j, const py::args &args, int z) { return py::make_tuple(i, j, args, z); },
|
||||
"i"_a,
|
||||
"j"_a,
|
||||
"z"_a);
|
||||
m.def(
|
||||
"args_kwonly_kwargs",
|
||||
[](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(i, j, args, z, kwargs);
|
||||
},
|
||||
"i"_a,
|
||||
"j"_a,
|
||||
py::kw_only{},
|
||||
"z"_a);
|
||||
m.def(
|
||||
"args_kwonly_kwargs_defaults",
|
||||
[](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(i, j, args, z, kwargs);
|
||||
},
|
||||
"i"_a = 1,
|
||||
"j"_a = 3.14159,
|
||||
"z"_a = 42);
|
||||
m.def(
|
||||
"args_kwonly_full_monty",
|
||||
[](int h, int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
|
||||
return py::make_tuple(h, i, j, args, z, kwargs);
|
||||
},
|
||||
py::arg() = 1,
|
||||
py::arg() = 2,
|
||||
py::pos_only{},
|
||||
"j"_a = 3.14159,
|
||||
"z"_a = 42);
|
||||
|
||||
|
||||
// test_args_refcount
|
||||
// PyPy needs a garbage collection to get the reference count values to match CPython's behaviour
|
||||
#ifdef PYPY_VERSION
|
||||
#define GC_IF_NEEDED ConstructorStats::gc()
|
||||
#else
|
||||
#define GC_IF_NEEDED
|
||||
#endif
|
||||
m.def("arg_refcount_h", [](py::handle h) { GC_IF_NEEDED; return h.ref_count(); });
|
||||
m.def("arg_refcount_h", [](py::handle h, py::handle, py::handle) { GC_IF_NEEDED; return h.ref_count(); });
|
||||
// test_args_refcount
|
||||
// PyPy needs a garbage collection to get the reference count values to match CPython's behaviour
|
||||
#ifdef PYPY_VERSION
|
||||
# define GC_IF_NEEDED ConstructorStats::gc()
|
||||
#else
|
||||
# define GC_IF_NEEDED
|
||||
#endif
|
||||
m.def("arg_refcount_h", [](py::handle h) {
|
||||
GC_IF_NEEDED;
|
||||
return h.ref_count();
|
||||
});
|
||||
m.def("arg_refcount_h", [](py::handle h, py::handle, py::handle) {
|
||||
GC_IF_NEEDED;
|
||||
return h.ref_count();
|
||||
});
|
||||
m.def("arg_refcount_o", [](const py::object &o) {
|
||||
GC_IF_NEEDED;
|
||||
return o.ref_count();
|
||||
@ -109,23 +138,42 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
|
||||
// pybind11 won't allow these to be bound: args and kwargs, if present, must be at the end.
|
||||
// Uncomment these to test that the static_assert is indeed working:
|
||||
// m.def("bad_args1", [](py::args, int) {});
|
||||
// m.def("bad_args2", [](py::kwargs, int) {});
|
||||
// m.def("bad_args3", [](py::kwargs, py::args) {});
|
||||
// m.def("bad_args4", [](py::args, int, py::kwargs) {});
|
||||
// m.def("bad_args5", [](py::args, py::kwargs, int) {});
|
||||
// m.def("bad_args6", [](py::args, py::args) {});
|
||||
// m.def("bad_args7", [](py::kwargs, py::kwargs) {});
|
||||
// m.def("bad_args1", [](py::args, int) {});
|
||||
// m.def("bad_args2", [](py::kwargs, int) {});
|
||||
// m.def("bad_args3", [](py::kwargs, py::args) {});
|
||||
// m.def("bad_args4", [](py::args, int, py::kwargs) {});
|
||||
// m.def("bad_args5", [](py::args, py::kwargs, int) {});
|
||||
// m.def("bad_args6", [](py::args, py::args) {});
|
||||
// m.def("bad_args7", [](py::kwargs, py::kwargs) {});
|
||||
|
||||
// test_keyword_only_args
|
||||
m.def("kw_only_all", [](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::kw_only(), py::arg("i"), py::arg("j"));
|
||||
m.def("kw_only_some", [](int i, int j, int k) { return py::make_tuple(i, j, k); },
|
||||
py::arg(), py::kw_only(), py::arg("j"), py::arg("k"));
|
||||
m.def("kw_only_with_defaults", [](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); },
|
||||
py::arg() = 3, "j"_a = 4, py::kw_only(), "k"_a = 5, "z"_a);
|
||||
m.def("kw_only_mixed", [](int i, int j) { return py::make_tuple(i, j); },
|
||||
"i"_a, py::kw_only(), "j"_a);
|
||||
m.def(
|
||||
"kw_only_all",
|
||||
[](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::kw_only(),
|
||||
py::arg("i"),
|
||||
py::arg("j"));
|
||||
m.def(
|
||||
"kw_only_some",
|
||||
[](int i, int j, int k) { return py::make_tuple(i, j, k); },
|
||||
py::arg(),
|
||||
py::kw_only(),
|
||||
py::arg("j"),
|
||||
py::arg("k"));
|
||||
m.def(
|
||||
"kw_only_with_defaults",
|
||||
[](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); },
|
||||
py::arg() = 3,
|
||||
"j"_a = 4,
|
||||
py::kw_only(),
|
||||
"k"_a = 5,
|
||||
"z"_a);
|
||||
m.def(
|
||||
"kw_only_mixed",
|
||||
[](int i, int j) { return py::make_tuple(i, j); },
|
||||
"i"_a,
|
||||
py::kw_only(),
|
||||
"j"_a);
|
||||
m.def(
|
||||
"kw_only_plus_more",
|
||||
[](int i, int j, int k, const py::kwargs &kwargs) {
|
||||
@ -137,31 +185,57 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
py::arg("k") /* kw-only */);
|
||||
|
||||
m.def("register_invalid_kw_only", [](py::module_ m) {
|
||||
m.def("bad_kw_only", [](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::kw_only(), py::arg() /* invalid unnamed argument */, "j"_a);
|
||||
m.def(
|
||||
"bad_kw_only",
|
||||
[](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::kw_only(),
|
||||
py::arg() /* invalid unnamed argument */,
|
||||
"j"_a);
|
||||
});
|
||||
|
||||
// test_positional_only_args
|
||||
m.def("pos_only_all", [](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::arg("i"), py::arg("j"), py::pos_only());
|
||||
m.def("pos_only_mix", [](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::arg("i"), py::pos_only(), py::arg("j"));
|
||||
m.def("pos_kw_only_mix", [](int i, int j, int k) { return py::make_tuple(i, j, k); },
|
||||
py::arg("i"), py::pos_only(), py::arg("j"), py::kw_only(), py::arg("k"));
|
||||
m.def("pos_only_def_mix", [](int i, int j, int k) { return py::make_tuple(i, j, k); },
|
||||
py::arg("i"), py::arg("j") = 2, py::pos_only(), py::arg("k") = 3);
|
||||
|
||||
m.def(
|
||||
"pos_only_all",
|
||||
[](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::arg("i"),
|
||||
py::arg("j"),
|
||||
py::pos_only());
|
||||
m.def(
|
||||
"pos_only_mix",
|
||||
[](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::arg("i"),
|
||||
py::pos_only(),
|
||||
py::arg("j"));
|
||||
m.def(
|
||||
"pos_kw_only_mix",
|
||||
[](int i, int j, int k) { return py::make_tuple(i, j, k); },
|
||||
py::arg("i"),
|
||||
py::pos_only(),
|
||||
py::arg("j"),
|
||||
py::kw_only(),
|
||||
py::arg("k"));
|
||||
m.def(
|
||||
"pos_only_def_mix",
|
||||
[](int i, int j, int k) { return py::make_tuple(i, j, k); },
|
||||
py::arg("i"),
|
||||
py::arg("j") = 2,
|
||||
py::pos_only(),
|
||||
py::arg("k") = 3);
|
||||
|
||||
// These should fail to compile:
|
||||
#ifdef PYBIND11_NEVER_DEFINED_EVER
|
||||
// argument annotations are required when using kw_only
|
||||
m.def("bad_kw_only1", [](int) {}, py::kw_only());
|
||||
m.def(
|
||||
"bad_kw_only1", [](int) {}, py::kw_only());
|
||||
// can't specify both `py::kw_only` and a `py::args` argument
|
||||
m.def("bad_kw_only2", [](int i, py::args) {}, py::kw_only(), "i"_a);
|
||||
m.def(
|
||||
"bad_kw_only2", [](int i, py::args) {}, py::kw_only(), "i"_a);
|
||||
#endif
|
||||
|
||||
// test_function_signatures (along with most of the above)
|
||||
struct KWClass { void foo(int, float) {} };
|
||||
struct KWClass {
|
||||
void foo(int, float) {}
|
||||
};
|
||||
py::class_<KWClass>(m, "KWClass")
|
||||
.def("foo0", &KWClass::foo)
|
||||
.def("foo1", &KWClass::foo, "x"_a, "y"_a);
|
||||
@ -182,11 +256,18 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
.def(py::init([](int) { return first_arg_kw_only(); }),
|
||||
py::kw_only(), // This being before any args was broken
|
||||
py::arg("i") = 0)
|
||||
.def("method", [](first_arg_kw_only&, int, int) {},
|
||||
py::kw_only(), // and likewise here
|
||||
py::arg("i") = 1, py::arg("j") = 2)
|
||||
.def(
|
||||
"method",
|
||||
[](first_arg_kw_only &, int, int) {},
|
||||
py::kw_only(), // and likewise here
|
||||
py::arg("i") = 1,
|
||||
py::arg("j") = 2)
|
||||
// Closely related: pos_only marker didn't show up properly when it was before any other
|
||||
// arguments (although that is fairly useless in practice).
|
||||
.def("pos_only", [](first_arg_kw_only&, int, int) {},
|
||||
py::pos_only{}, py::arg("i"), py::arg("j"));
|
||||
.def(
|
||||
"pos_only",
|
||||
[](first_arg_kw_only &, int, int) {},
|
||||
py::pos_only{},
|
||||
py::arg("i"),
|
||||
py::arg("j"));
|
||||
}
|
||||
|
||||
@ -8,12 +8,12 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "local_bindings.h"
|
||||
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
#include "local_bindings.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
@ -24,9 +24,9 @@ TEST_SUBMODULE(local_bindings, m) {
|
||||
|
||||
// test_local_bindings
|
||||
// Register a class with py::module_local:
|
||||
bind_local<LocalType, -1>(m, "LocalType", py::module_local())
|
||||
.def("get3", [](LocalType &t) { return t.i + 3; })
|
||||
;
|
||||
bind_local<LocalType, -1>(m, "LocalType", py::module_local()).def("get3", [](LocalType &t) {
|
||||
return t.i + 3;
|
||||
});
|
||||
|
||||
m.def("local_value", [](LocalType &l) { return l.i; });
|
||||
|
||||
@ -35,14 +35,14 @@ TEST_SUBMODULE(local_bindings, m) {
|
||||
// one, in pybind11_cross_module_tests.cpp, is designed to fail):
|
||||
bind_local<NonLocalType, 0>(m, "NonLocalType")
|
||||
.def(py::init<int>())
|
||||
.def("get", [](LocalType &i) { return i.i; })
|
||||
;
|
||||
.def("get", [](LocalType &i) { return i.i; });
|
||||
|
||||
// test_duplicate_local
|
||||
// py::module_local declarations should be visible across compilation units that get linked together;
|
||||
// this tries to register a duplicate local. It depends on a definition in test_class.cpp and
|
||||
// should raise a runtime error from the duplicate definition attempt. If test_class isn't
|
||||
// available it *also* throws a runtime error (with "test_class not enabled" as value).
|
||||
// py::module_local declarations should be visible across compilation units that get linked
|
||||
// together; this tries to register a duplicate local. It depends on a definition in
|
||||
// test_class.cpp and should raise a runtime error from the duplicate definition attempt. If
|
||||
// test_class isn't available it *also* throws a runtime error (with "test_class not enabled"
|
||||
// as value).
|
||||
m.def("register_local_external", [m]() {
|
||||
auto main = py::module_::import("pybind11_tests");
|
||||
if (py::hasattr(main, "class_")) {
|
||||
@ -79,12 +79,12 @@ TEST_SUBMODULE(local_bindings, m) {
|
||||
m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); });
|
||||
|
||||
// test_internal_locals_differ
|
||||
m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; });
|
||||
m.def("local_cpp_types_addr",
|
||||
[]() { return (uintptr_t) &py::detail::get_local_internals().registered_types_cpp; });
|
||||
|
||||
// test_stl_caster_vs_stl_bind
|
||||
m.def("load_vector_via_caster", [](std::vector<int> v) {
|
||||
return std::accumulate(v.begin(), v.end(), 0);
|
||||
});
|
||||
m.def("load_vector_via_caster",
|
||||
[](std::vector<int> v) { return std::accumulate(v.begin(), v.end(), 0); });
|
||||
|
||||
// test_cross_module_calls
|
||||
m.def("return_self", [](LocalVec *v) { return v; });
|
||||
@ -94,11 +94,9 @@ TEST_SUBMODULE(local_bindings, m) {
|
||||
public:
|
||||
explicit Cat(std::string name) : Pet(std::move(name)) {}
|
||||
};
|
||||
py::class_<pets::Pet>(m, "Pet", py::module_local())
|
||||
.def("get_name", &pets::Pet::name);
|
||||
py::class_<pets::Pet>(m, "Pet", py::module_local()).def("get_name", &pets::Pet::name);
|
||||
// Binding for local extending class:
|
||||
py::class_<Cat, pets::Pet>(m, "Cat")
|
||||
.def(py::init<std::string>());
|
||||
py::class_<Cat, pets::Pet>(m, "Cat").def(py::init<std::string>());
|
||||
m.def("pet_name", [](pets::Pet &p) { return p.name(); });
|
||||
|
||||
py::class_<MixGL>(m, "MixGL").def(py::init<int>());
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#if !defined(PYBIND11_OVERLOAD_CAST)
|
||||
template <typename... Args>
|
||||
@ -27,7 +27,10 @@ public:
|
||||
|
||||
std::string toString() const { return "ExampleMandA[value=" + std::to_string(value) + "]"; }
|
||||
|
||||
void operator=(const ExampleMandA &e) { print_copy_assigned(this); value = e.value; }
|
||||
void operator=(const ExampleMandA &e) {
|
||||
print_copy_assigned(this);
|
||||
value = e.value;
|
||||
}
|
||||
void operator=(ExampleMandA &&e) noexcept {
|
||||
print_move_assigned(this);
|
||||
value = e.value;
|
||||
@ -40,37 +43,37 @@ public:
|
||||
void add4(ExampleMandA *other) { value += other->value; } // passing by pointer
|
||||
void add5(const ExampleMandA *other) { value += other->value; } // passing by const pointer
|
||||
|
||||
void add6(int other) { value += other; } // passing by value
|
||||
void add7(int &other) { value += other; } // passing by reference
|
||||
void add8(const int &other) { value += other; } // passing by const reference
|
||||
void add6(int other) { value += other; } // passing by value
|
||||
void add7(int &other) { value += other; } // passing by reference
|
||||
void add8(const int &other) { value += other; } // passing by const reference
|
||||
// NOLINTNEXTLINE(readability-non-const-parameter) Deliberately non-const for testing
|
||||
void add9(int *other) { value += *other; } // passing by pointer
|
||||
void add10(const int *other) { value += *other; } // passing by const pointer
|
||||
void add9(int *other) { value += *other; } // passing by pointer
|
||||
void add10(const int *other) { value += *other; } // passing by const pointer
|
||||
|
||||
void consume_str(std::string&&) {}
|
||||
void consume_str(std::string &&) {}
|
||||
|
||||
ExampleMandA self1() { return *this; } // return by value
|
||||
ExampleMandA &self2() { return *this; } // return by reference
|
||||
const ExampleMandA &self3() const { return *this; } // return by const reference
|
||||
ExampleMandA *self4() { return this; } // return by pointer
|
||||
const ExampleMandA *self5() const { return this; } // return by const pointer
|
||||
ExampleMandA self1() { return *this; } // return by value
|
||||
ExampleMandA &self2() { return *this; } // return by reference
|
||||
const ExampleMandA &self3() const { return *this; } // return by const reference
|
||||
ExampleMandA *self4() { return this; } // return by pointer
|
||||
const ExampleMandA *self5() const { return this; } // return by const pointer
|
||||
|
||||
int internal1() const { return value; } // return by value
|
||||
int &internal2() { return value; } // return by reference
|
||||
const int &internal3() const { return value; } // return by const reference
|
||||
int *internal4() { return &value; } // return by pointer
|
||||
const int *internal5() { return &value; } // return by const pointer
|
||||
int internal1() const { return value; } // return by value
|
||||
int &internal2() { return value; } // return by reference
|
||||
const int &internal3() const { return value; } // return by const reference
|
||||
int *internal4() { return &value; } // return by pointer
|
||||
const int *internal5() { return &value; } // return by const pointer
|
||||
|
||||
py::str overloaded() { return "()"; }
|
||||
py::str overloaded(int) { return "(int)"; }
|
||||
py::str overloaded(int, float) { return "(int, float)"; }
|
||||
py::str overloaded(float, int) { return "(float, int)"; }
|
||||
py::str overloaded(int, int) { return "(int, int)"; }
|
||||
py::str overloaded() { return "()"; }
|
||||
py::str overloaded(int) { return "(int)"; }
|
||||
py::str overloaded(int, float) { return "(int, float)"; }
|
||||
py::str overloaded(float, int) { return "(float, int)"; }
|
||||
py::str overloaded(int, int) { return "(int, int)"; }
|
||||
py::str overloaded(float, float) { return "(float, float)"; }
|
||||
py::str overloaded(int) const { return "(int) const"; }
|
||||
py::str overloaded(int, float) const { return "(int, float) const"; }
|
||||
py::str overloaded(float, int) const { return "(float, int) const"; }
|
||||
py::str overloaded(int, int) const { return "(int, int) const"; }
|
||||
py::str overloaded(int) const { return "(int) const"; }
|
||||
py::str overloaded(int, float) const { return "(int, float) const"; }
|
||||
py::str overloaded(float, int) const { return "(float, int) const"; }
|
||||
py::str overloaded(int, int) const { return "(int, int) const"; }
|
||||
py::str overloaded(float, float) const { return "(float, float) const"; }
|
||||
|
||||
static py::str overloaded(float) { return "static float"; }
|
||||
@ -112,7 +115,10 @@ UserType TestPropRVP::sv1(1);
|
||||
UserType TestPropRVP::sv2(1);
|
||||
|
||||
// Test None-allowed py::arg argument policy
|
||||
class NoneTester { public: int answer = 42; };
|
||||
class NoneTester {
|
||||
public:
|
||||
int answer = 42;
|
||||
};
|
||||
int none1(const NoneTester &obj) { return obj.answer; }
|
||||
int none2(NoneTester *obj) { return obj ? obj->answer : -1; }
|
||||
int none3(std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
|
||||
@ -134,11 +140,15 @@ struct StrIssue {
|
||||
explicit StrIssue(int i) : val{i} {}
|
||||
};
|
||||
|
||||
// Issues #854, #910: incompatible function args when member function/pointer is in unregistered base class
|
||||
// Issues #854, #910: incompatible function args when member function/pointer is in unregistered
|
||||
// base class
|
||||
class UnregisteredBase {
|
||||
public:
|
||||
void do_nothing() const {}
|
||||
void increase_value() { rw_value++; ro_value += 0.25; }
|
||||
void increase_value() {
|
||||
rw_value++;
|
||||
ro_value += 0.25;
|
||||
}
|
||||
void set_int(int v) { rw_value = v; }
|
||||
int get_int() const { return rw_value; }
|
||||
double get_double() const { return ro_value; }
|
||||
@ -161,10 +171,10 @@ struct RefQualified {
|
||||
|
||||
// Test rvalue ref param
|
||||
struct RValueRefParam {
|
||||
std::size_t func1(std::string&& s) { return s.size(); }
|
||||
std::size_t func2(std::string&& s) const { return s.size(); }
|
||||
std::size_t func3(std::string&& s) & { return s.size(); }
|
||||
std::size_t func4(std::string&& s) const & { return s.size(); }
|
||||
std::size_t func1(std::string &&s) { return s.size(); }
|
||||
std::size_t func2(std::string &&s) const { return s.size(); }
|
||||
std::size_t func3(std::string &&s) & { return s.size(); }
|
||||
std::size_t func4(std::string &&s) const & { return s.size(); }
|
||||
};
|
||||
|
||||
TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
@ -172,8 +182,8 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
py::class_<ExampleMandA> emna(m, "ExampleMandA");
|
||||
emna.def(py::init<>())
|
||||
.def(py::init<int>())
|
||||
.def(py::init<std::string&&>())
|
||||
.def(py::init<const ExampleMandA&>())
|
||||
.def(py::init<std::string &&>())
|
||||
.def(py::init<const ExampleMandA &>())
|
||||
.def("add1", &ExampleMandA::add1)
|
||||
.def("add2", &ExampleMandA::add2)
|
||||
.def("add3", &ExampleMandA::add3)
|
||||
@ -198,16 +208,20 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
#if defined(PYBIND11_OVERLOAD_CAST)
|
||||
.def("overloaded", py::overload_cast<>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<int>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<int, float>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<float, int>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<int, int>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<int, float>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<float, int>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<int, int>(&ExampleMandA::overloaded))
|
||||
.def("overloaded", py::overload_cast<float, float>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_float", py::overload_cast<float, float>(&ExampleMandA::overloaded))
|
||||
.def("overloaded_const", py::overload_cast<int >(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", py::overload_cast<int, float>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", py::overload_cast<float, int>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", py::overload_cast<int, int>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", py::overload_cast<float, float>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const", py::overload_cast<int>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const",
|
||||
py::overload_cast<int, float>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const",
|
||||
py::overload_cast<float, int>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const",
|
||||
py::overload_cast<int, int>(&ExampleMandA::overloaded, py::const_))
|
||||
.def("overloaded_const",
|
||||
py::overload_cast<float, float>(&ExampleMandA::overloaded, py::const_))
|
||||
#else
|
||||
// Use both the traditional static_cast method and the C++11 compatible overload_cast_
|
||||
.def("overloaded", overload_cast_<>()(&ExampleMandA::overloaded))
|
||||
@ -225,16 +239,29 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
#endif
|
||||
// test_no_mixed_overloads
|
||||
// Raise error if trying to mix static/non-static overloads on the same name:
|
||||
.def_static("add_mixed_overloads1", []() {
|
||||
auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module_::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA"));
|
||||
emna.def ("overload_mixed1", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded))
|
||||
.def_static("overload_mixed1", static_cast<py::str ( *)(float )>(&ExampleMandA::overloaded));
|
||||
})
|
||||
.def_static("add_mixed_overloads2", []() {
|
||||
auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module_::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA"));
|
||||
emna.def_static("overload_mixed2", static_cast<py::str ( *)(float )>(&ExampleMandA::overloaded))
|
||||
.def ("overload_mixed2", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded));
|
||||
})
|
||||
.def_static("add_mixed_overloads1",
|
||||
[]() {
|
||||
auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(
|
||||
py::module_::import("pybind11_tests.methods_and_attributes")
|
||||
.attr("ExampleMandA"));
|
||||
emna.def("overload_mixed1",
|
||||
static_cast<py::str (ExampleMandA::*)(int, int)>(
|
||||
&ExampleMandA::overloaded))
|
||||
.def_static(
|
||||
"overload_mixed1",
|
||||
static_cast<py::str (*)(float)>(&ExampleMandA::overloaded));
|
||||
})
|
||||
.def_static("add_mixed_overloads2",
|
||||
[]() {
|
||||
auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(
|
||||
py::module_::import("pybind11_tests.methods_and_attributes")
|
||||
.attr("ExampleMandA"));
|
||||
emna.def_static("overload_mixed2",
|
||||
static_cast<py::str (*)(float)>(&ExampleMandA::overloaded))
|
||||
.def("overload_mixed2",
|
||||
static_cast<py::str (ExampleMandA::*)(int, int)>(
|
||||
&ExampleMandA::overloaded));
|
||||
})
|
||||
.def("__str__", &ExampleMandA::toString)
|
||||
.def_readwrite("value", &ExampleMandA::value);
|
||||
|
||||
@ -307,7 +334,7 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
[](const py::object &) { return UserType(1); });
|
||||
|
||||
// test_metaclass_override
|
||||
struct MetaclassOverride { };
|
||||
struct MetaclassOverride {};
|
||||
py::class_<MetaclassOverride>(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type))
|
||||
.def_property_readonly_static("readonly", [](const py::object &) { return 1; });
|
||||
|
||||
@ -315,22 +342,21 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
m.def("overload_order", [](const std::string &) { return 1; });
|
||||
m.def("overload_order", [](const std::string &) { return 2; });
|
||||
m.def("overload_order", [](int) { return 3; });
|
||||
m.def("overload_order", [](int) { return 4; }, py::prepend{});
|
||||
m.def(
|
||||
"overload_order", [](int) { return 4; }, py::prepend{});
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
// test_dynamic_attributes
|
||||
class DynamicClass {
|
||||
public:
|
||||
DynamicClass() { print_default_created(this); }
|
||||
DynamicClass(const DynamicClass&) = delete;
|
||||
DynamicClass(const DynamicClass &) = delete;
|
||||
~DynamicClass() { print_destroyed(this); }
|
||||
};
|
||||
py::class_<DynamicClass>(m, "DynamicClass", py::dynamic_attr())
|
||||
.def(py::init());
|
||||
py::class_<DynamicClass>(m, "DynamicClass", py::dynamic_attr()).def(py::init());
|
||||
|
||||
class CppDerivedDynamicClass : public DynamicClass { };
|
||||
py::class_<CppDerivedDynamicClass, DynamicClass>(m, "CppDerivedDynamicClass")
|
||||
.def(py::init());
|
||||
class CppDerivedDynamicClass : public DynamicClass {};
|
||||
py::class_<CppDerivedDynamicClass, DynamicClass>(m, "CppDerivedDynamicClass").def(py::init());
|
||||
#endif
|
||||
|
||||
// test_bad_arg_default
|
||||
@ -340,20 +366,27 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
#else
|
||||
m.attr("debug_enabled") = false;
|
||||
#endif
|
||||
m.def("bad_arg_def_named", []{
|
||||
m.def("bad_arg_def_named", [] {
|
||||
auto m = py::module_::import("pybind11_tests");
|
||||
m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg("a") = UnregisteredType());
|
||||
m.def(
|
||||
"should_fail",
|
||||
[](int, UnregisteredType) {},
|
||||
py::arg(),
|
||||
py::arg("a") = UnregisteredType());
|
||||
});
|
||||
m.def("bad_arg_def_unnamed", []{
|
||||
m.def("bad_arg_def_unnamed", [] {
|
||||
auto m = py::module_::import("pybind11_tests");
|
||||
m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg() = UnregisteredType());
|
||||
m.def(
|
||||
"should_fail",
|
||||
[](int, UnregisteredType) {},
|
||||
py::arg(),
|
||||
py::arg() = UnregisteredType());
|
||||
});
|
||||
|
||||
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
|
||||
|
||||
// test_accepts_none
|
||||
py::class_<NoneTester, std::shared_ptr<NoneTester>>(m, "NoneTester")
|
||||
.def(py::init<>());
|
||||
py::class_<NoneTester, std::shared_ptr<NoneTester>>(m, "NoneTester").def(py::init<>());
|
||||
m.def("no_none1", &none1, py::arg{}.none(false));
|
||||
m.def("no_none2", &none2, py::arg{}.none(false));
|
||||
m.def("no_none3", &none3, py::arg{}.none(false));
|
||||
@ -371,21 +404,19 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
// test_casts_none
|
||||
// Issue #2778: implicit casting from None to object (not pointer)
|
||||
py::class_<NoneCastTester>(m, "NoneCastTester")
|
||||
.def(py::init<>())
|
||||
.def(py::init<int>())
|
||||
.def(py::init([](py::none const&) { return NoneCastTester{}; }));
|
||||
.def(py::init<>())
|
||||
.def(py::init<int>())
|
||||
.def(py::init([](py::none const &) { return NoneCastTester{}; }));
|
||||
py::implicitly_convertible<py::none, NoneCastTester>();
|
||||
m.def("ok_obj_or_none", [](NoneCastTester const& foo) { return foo.answer; });
|
||||
|
||||
m.def("ok_obj_or_none", [](NoneCastTester const &foo) { return foo.answer; });
|
||||
|
||||
// test_str_issue
|
||||
// Issue #283: __str__ called on uninitialized instance when constructor arguments invalid
|
||||
py::class_<StrIssue>(m, "StrIssue")
|
||||
.def(py::init<int>())
|
||||
.def(py::init<>())
|
||||
.def("__str__", [](const StrIssue &si) {
|
||||
return "StrIssue[" + std::to_string(si.val) + "]"; }
|
||||
);
|
||||
.def("__str__",
|
||||
[](const StrIssue &si) { return "StrIssue[" + std::to_string(si.val) + "]"; });
|
||||
|
||||
// test_unregistered_base_implementations
|
||||
//
|
||||
@ -408,7 +439,8 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
// This one is in the registered class:
|
||||
.def("sum", &RegisteredDerived::sum);
|
||||
|
||||
using Adapted = decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing));
|
||||
using Adapted
|
||||
= decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing));
|
||||
static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, "");
|
||||
|
||||
// test_methods_and_attributes
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
TEST_SUBMODULE(modules, m) {
|
||||
// test_nested_modules
|
||||
@ -22,23 +22,30 @@ TEST_SUBMODULE(modules, m) {
|
||||
public:
|
||||
explicit A(int v) : v(v) { print_created(this, v); }
|
||||
~A() { print_destroyed(this); }
|
||||
A(const A&) { print_copy_created(this); }
|
||||
A& operator=(const A ©) { print_copy_assigned(this); v = copy.v; return *this; }
|
||||
A(const A &) { print_copy_created(this); }
|
||||
A &operator=(const A ©) {
|
||||
print_copy_assigned(this);
|
||||
v = copy.v;
|
||||
return *this;
|
||||
}
|
||||
std::string toString() const { return "A[" + std::to_string(v) + "]"; }
|
||||
|
||||
private:
|
||||
int v;
|
||||
};
|
||||
py::class_<A>(m_sub, "A")
|
||||
.def(py::init<int>())
|
||||
.def("__repr__", &A::toString);
|
||||
py::class_<A>(m_sub, "A").def(py::init<int>()).def("__repr__", &A::toString);
|
||||
|
||||
class B {
|
||||
public:
|
||||
B() { print_default_created(this); }
|
||||
~B() { print_destroyed(this); }
|
||||
B(const B&) { print_copy_created(this); }
|
||||
B& operator=(const B ©) { print_copy_assigned(this); a1 = copy.a1; a2 = copy.a2; return *this; }
|
||||
B(const B &) { print_copy_created(this); }
|
||||
B &operator=(const B ©) {
|
||||
print_copy_assigned(this);
|
||||
a1 = copy.a1;
|
||||
a2 = copy.a2;
|
||||
return *this;
|
||||
}
|
||||
A &get_a1() { return a1; }
|
||||
A &get_a2() { return a2; }
|
||||
|
||||
@ -47,10 +54,16 @@ TEST_SUBMODULE(modules, m) {
|
||||
};
|
||||
py::class_<B>(m_sub, "B")
|
||||
.def(py::init<>())
|
||||
.def("get_a1", &B::get_a1, "Return the internal A 1", py::return_value_policy::reference_internal)
|
||||
.def("get_a2", &B::get_a2, "Return the internal A 2", py::return_value_policy::reference_internal)
|
||||
.def_readwrite("a1", &B::a1) // def_readonly uses an internal
|
||||
// reference return policy by default
|
||||
.def("get_a1",
|
||||
&B::get_a1,
|
||||
"Return the internal A 1",
|
||||
py::return_value_policy::reference_internal)
|
||||
.def("get_a2",
|
||||
&B::get_a2,
|
||||
"Return the internal A 2",
|
||||
py::return_value_policy::reference_internal)
|
||||
.def_readwrite("a1", &B::a1) // def_readonly uses an internal
|
||||
// reference return policy by default
|
||||
.def_readwrite("a2", &B::a2);
|
||||
|
||||
// This is intentionally "py::module" to verify it still can be used in place of "py::module_"
|
||||
@ -59,13 +72,14 @@ TEST_SUBMODULE(modules, m) {
|
||||
// test_duplicate_registration
|
||||
// Registering two things with the same name
|
||||
m.def("duplicate_registration", []() {
|
||||
class Dupe1 { };
|
||||
class Dupe2 { };
|
||||
class Dupe3 { };
|
||||
class DupeException { };
|
||||
class Dupe1 {};
|
||||
class Dupe2 {};
|
||||
class Dupe3 {};
|
||||
class DupeException {};
|
||||
|
||||
// Go ahead and leak, until we have a non-leaking py::module_ constructor
|
||||
auto dm = py::module_::create_extension_module("dummy", nullptr, new py::module_::module_def);
|
||||
auto dm
|
||||
= py::module_::create_extension_module("dummy", nullptr, new py::module_::module_def);
|
||||
auto failures = py::list();
|
||||
|
||||
py::class_<Dupe1>(dm, "Dupe1");
|
||||
@ -76,27 +90,33 @@ TEST_SUBMODULE(modules, m) {
|
||||
try {
|
||||
py::class_<Dupe1>(dm, "Dupe1");
|
||||
failures.append("Dupe1 class");
|
||||
} catch (std::runtime_error &) {}
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
dm.def("Dupe1", []() { return Dupe1(); });
|
||||
failures.append("Dupe1 function");
|
||||
} catch (std::runtime_error &) {}
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
py::class_<Dupe3>(dm, "dupe1_factory");
|
||||
failures.append("dupe1_factory");
|
||||
} catch (std::runtime_error &) {}
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
py::exception<Dupe3>(dm, "Dupe2");
|
||||
failures.append("Dupe2");
|
||||
} catch (std::runtime_error &) {}
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
dm.def("DupeException", []() { return 30; });
|
||||
failures.append("DupeException1");
|
||||
} catch (std::runtime_error &) {}
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
try {
|
||||
py::class_<DupeException>(dm, "DupeException");
|
||||
failures.append("DupeException2");
|
||||
} catch (std::runtime_error &) {}
|
||||
} catch (std::runtime_error &) {
|
||||
}
|
||||
|
||||
return failures;
|
||||
});
|
||||
|
||||
@ -8,14 +8,15 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Many bases for testing that multiple inheritance from many classes (i.e. requiring extra
|
||||
// space for holder constructed flags) works.
|
||||
template <int N> struct BaseN {
|
||||
template <int N>
|
||||
struct BaseN {
|
||||
explicit BaseN(int i) : i(i) {}
|
||||
int i;
|
||||
};
|
||||
@ -57,13 +58,23 @@ struct Base2a {
|
||||
int i;
|
||||
};
|
||||
struct Base12a : Base1a, Base2a {
|
||||
Base12a(int i, int j) : Base1a(i), Base2a(j) { }
|
||||
Base12a(int i, int j) : Base1a(i), Base2a(j) {}
|
||||
};
|
||||
|
||||
// test_mi_unaligned_base
|
||||
// test_mi_base_return
|
||||
struct I801B1 { int a = 1; I801B1() = default; I801B1(const I801B1 &) = default; virtual ~I801B1() = default; };
|
||||
struct I801B2 { int b = 2; I801B2() = default; I801B2(const I801B2 &) = default; virtual ~I801B2() = default; };
|
||||
struct I801B1 {
|
||||
int a = 1;
|
||||
I801B1() = default;
|
||||
I801B1(const I801B1 &) = default;
|
||||
virtual ~I801B1() = default;
|
||||
};
|
||||
struct I801B2 {
|
||||
int b = 2;
|
||||
I801B2() = default;
|
||||
I801B2(const I801B2 &) = default;
|
||||
virtual ~I801B2() = default;
|
||||
};
|
||||
struct I801C : I801B1, I801B2 {};
|
||||
struct I801D : I801C {}; // Indirect MI
|
||||
|
||||
@ -82,8 +93,7 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
int i;
|
||||
};
|
||||
py::class_<Base1> b1(m, "Base1");
|
||||
b1.def(py::init<int>())
|
||||
.def("foo", &Base1::foo);
|
||||
b1.def(py::init<int>()).def("foo", &Base1::foo);
|
||||
|
||||
struct Base2 {
|
||||
explicit Base2(int i) : i(i) {}
|
||||
@ -91,42 +101,49 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
int i;
|
||||
};
|
||||
py::class_<Base2> b2(m, "Base2");
|
||||
b2.def(py::init<int>())
|
||||
.def("bar", &Base2::bar);
|
||||
|
||||
b2.def(py::init<int>()).def("bar", &Base2::bar);
|
||||
|
||||
// test_multiple_inheritance_cpp
|
||||
struct Base12 : Base1, Base2 {
|
||||
Base12(int i, int j) : Base1(i), Base2(j) { }
|
||||
Base12(int i, int j) : Base1(i), Base2(j) {}
|
||||
};
|
||||
struct MIType : Base12 {
|
||||
MIType(int i, int j) : Base12(i, j) { }
|
||||
MIType(int i, int j) : Base12(i, j) {}
|
||||
};
|
||||
py::class_<Base12, Base1, Base2>(m, "Base12");
|
||||
py::class_<MIType, Base12>(m, "MIType")
|
||||
.def(py::init<int, int>());
|
||||
|
||||
py::class_<MIType, Base12>(m, "MIType").def(py::init<int, int>());
|
||||
|
||||
// test_multiple_inheritance_python_many_bases
|
||||
#define PYBIND11_BASEN(N) \
|
||||
py::class_<BaseN<(N)>>(m, "BaseN" #N).def(py::init<int>()).def("f" #N, [](BaseN<N> &b) { \
|
||||
return b.i + (N); \
|
||||
})
|
||||
PYBIND11_BASEN( 1); PYBIND11_BASEN( 2); PYBIND11_BASEN( 3); PYBIND11_BASEN( 4);
|
||||
PYBIND11_BASEN( 5); PYBIND11_BASEN( 6); PYBIND11_BASEN( 7); PYBIND11_BASEN( 8);
|
||||
PYBIND11_BASEN( 9); PYBIND11_BASEN(10); PYBIND11_BASEN(11); PYBIND11_BASEN(12);
|
||||
PYBIND11_BASEN(13); PYBIND11_BASEN(14); PYBIND11_BASEN(15); PYBIND11_BASEN(16);
|
||||
PYBIND11_BASEN(1);
|
||||
PYBIND11_BASEN(2);
|
||||
PYBIND11_BASEN(3);
|
||||
PYBIND11_BASEN(4);
|
||||
PYBIND11_BASEN(5);
|
||||
PYBIND11_BASEN(6);
|
||||
PYBIND11_BASEN(7);
|
||||
PYBIND11_BASEN(8);
|
||||
PYBIND11_BASEN(9);
|
||||
PYBIND11_BASEN(10);
|
||||
PYBIND11_BASEN(11);
|
||||
PYBIND11_BASEN(12);
|
||||
PYBIND11_BASEN(13);
|
||||
PYBIND11_BASEN(14);
|
||||
PYBIND11_BASEN(15);
|
||||
PYBIND11_BASEN(16);
|
||||
PYBIND11_BASEN(17);
|
||||
|
||||
// Uncommenting this should result in a compile time failure (MI can only be specified via
|
||||
// template parameters because pybind has to know the types involved; see discussion in #742 for
|
||||
// details).
|
||||
// struct Base12v2 : Base1, Base2 {
|
||||
// Base12v2(int i, int j) : Base1(i), Base2(j) { }
|
||||
// };
|
||||
// py::class_<Base12v2>(m, "Base12v2", b1, b2)
|
||||
// .def(py::init<int, int>());
|
||||
|
||||
// template parameters because pybind has to know the types involved; see discussion in #742
|
||||
// for details).
|
||||
// struct Base12v2 : Base1, Base2 {
|
||||
// Base12v2(int i, int j) : Base1(i), Base2(j) { }
|
||||
// };
|
||||
// py::class_<Base12v2>(m, "Base12v2", b1, b2)
|
||||
// .def(py::init<int, int>());
|
||||
|
||||
// test_multiple_inheritance_virtbase
|
||||
// Test the case where not all base classes are specified, and where pybind11 requires the
|
||||
@ -139,8 +156,8 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
.def(py::init<int>())
|
||||
.def("bar", &Base2a::bar);
|
||||
|
||||
py::class_<Base12a, /* Base1 missing */ Base2a,
|
||||
std::shared_ptr<Base12a>>(m, "Base12a", py::multiple_inheritance())
|
||||
py::class_<Base12a, /* Base1 missing */ Base2a, std::shared_ptr<Base12a>>(
|
||||
m, "Base12a", py::multiple_inheritance())
|
||||
.def(py::init<int, int>());
|
||||
|
||||
m.def("bar_base2a", [](Base2a *b) { return b->bar(); });
|
||||
@ -150,11 +167,18 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
// test_mi_base_return
|
||||
// Issue #801: invalid casting to derived type with MI bases
|
||||
// Unregistered classes:
|
||||
struct I801B3 { int c = 3; virtual ~I801B3() = default; };
|
||||
struct I801B3 {
|
||||
int c = 3;
|
||||
virtual ~I801B3() = default;
|
||||
};
|
||||
struct I801E : I801B3, I801D {};
|
||||
|
||||
py::class_<I801B1, std::shared_ptr<I801B1>>(m, "I801B1").def(py::init<>()).def_readonly("a", &I801B1::a);
|
||||
py::class_<I801B2, std::shared_ptr<I801B2>>(m, "I801B2").def(py::init<>()).def_readonly("b", &I801B2::b);
|
||||
py::class_<I801B1, std::shared_ptr<I801B1>>(m, "I801B1")
|
||||
.def(py::init<>())
|
||||
.def_readonly("a", &I801B1::a);
|
||||
py::class_<I801B2, std::shared_ptr<I801B2>>(m, "I801B2")
|
||||
.def(py::init<>())
|
||||
.def_readonly("b", &I801B2::b);
|
||||
py::class_<I801C, I801B1, I801B2, std::shared_ptr<I801C>>(m, "I801C").def(py::init<>());
|
||||
py::class_<I801D, I801C, std::shared_ptr<I801D>>(m, "I801D").def(py::init<>());
|
||||
|
||||
@ -179,11 +203,8 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
m.def("i801e_c", []() -> I801C * { return new I801E(); });
|
||||
m.def("i801e_b2", []() -> I801B2 * { return new I801E(); });
|
||||
|
||||
|
||||
// test_mi_static_properties
|
||||
py::class_<Vanilla>(m, "Vanilla")
|
||||
.def(py::init<>())
|
||||
.def("vanilla", &Vanilla::vanilla);
|
||||
py::class_<Vanilla>(m, "Vanilla").def(py::init<>()).def("vanilla", &Vanilla::vanilla);
|
||||
|
||||
py::class_<WithStatic1>(m, "WithStatic1")
|
||||
.def(py::init<>())
|
||||
@ -195,22 +216,19 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
.def_static("static_func2", &WithStatic2::static_func2)
|
||||
.def_readwrite_static("static_value2", &WithStatic2::static_value2);
|
||||
|
||||
py::class_<VanillaStaticMix1, Vanilla, WithStatic1, WithStatic2>(
|
||||
m, "VanillaStaticMix1")
|
||||
py::class_<VanillaStaticMix1, Vanilla, WithStatic1, WithStatic2>(m, "VanillaStaticMix1")
|
||||
.def(py::init<>())
|
||||
.def_static("static_func", &VanillaStaticMix1::static_func)
|
||||
.def_readwrite_static("static_value", &VanillaStaticMix1::static_value);
|
||||
|
||||
py::class_<VanillaStaticMix2, WithStatic1, Vanilla, WithStatic2>(
|
||||
m, "VanillaStaticMix2")
|
||||
py::class_<VanillaStaticMix2, WithStatic1, Vanilla, WithStatic2>(m, "VanillaStaticMix2")
|
||||
.def(py::init<>())
|
||||
.def_static("static_func", &VanillaStaticMix2::static_func)
|
||||
.def_readwrite_static("static_value", &VanillaStaticMix2::static_value);
|
||||
|
||||
|
||||
struct WithDict { };
|
||||
struct VanillaDictMix1 : Vanilla, WithDict { };
|
||||
struct VanillaDictMix2 : WithDict, Vanilla { };
|
||||
struct WithDict {};
|
||||
struct VanillaDictMix1 : Vanilla, WithDict {};
|
||||
struct VanillaDictMix2 : WithDict, Vanilla {};
|
||||
py::class_<WithDict>(m, "WithDict", py::dynamic_attr()).def(py::init<>());
|
||||
py::class_<VanillaDictMix1, Vanilla, WithDict>(m, "VanillaDictMix1").def(py::init<>());
|
||||
py::class_<VanillaDictMix2, WithDict, Vanilla>(m, "VanillaDictMix2").def(py::init<>());
|
||||
@ -218,18 +236,25 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
// test_diamond_inheritance
|
||||
// Issue #959: segfault when constructing diamond inheritance instance
|
||||
// All of these have int members so that there will be various unequal pointers involved.
|
||||
struct B { int b; B() = default; B(const B&) = default; virtual ~B() = default; };
|
||||
struct C0 : public virtual B { int c0; };
|
||||
struct C1 : public virtual B { int c1; };
|
||||
struct D : public C0, public C1 { int d; };
|
||||
py::class_<B>(m, "B")
|
||||
.def("b", [](B *self) { return self; });
|
||||
py::class_<C0, B>(m, "C0")
|
||||
.def("c0", [](C0 *self) { return self; });
|
||||
py::class_<C1, B>(m, "C1")
|
||||
.def("c1", [](C1 *self) { return self; });
|
||||
py::class_<D, C0, C1>(m, "D")
|
||||
.def(py::init<>());
|
||||
struct B {
|
||||
int b;
|
||||
B() = default;
|
||||
B(const B &) = default;
|
||||
virtual ~B() = default;
|
||||
};
|
||||
struct C0 : public virtual B {
|
||||
int c0;
|
||||
};
|
||||
struct C1 : public virtual B {
|
||||
int c1;
|
||||
};
|
||||
struct D : public C0, public C1 {
|
||||
int d;
|
||||
};
|
||||
py::class_<B>(m, "B").def("b", [](B *self) { return self; });
|
||||
py::class_<C0, B>(m, "C0").def("c0", [](C0 *self) { return self; });
|
||||
py::class_<C1, B>(m, "C1").def("c1", [](C1 *self) { return self; });
|
||||
py::class_<D, C0, C1>(m, "D").def(py::init<>());
|
||||
|
||||
// test_pr3635_diamond_*
|
||||
// - functions are get_{base}_{var}, return {var}
|
||||
|
||||
@ -7,11 +7,11 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <pybind11/numpy.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
@ -22,7 +22,7 @@ struct DtypeCheck {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
DtypeCheck get_dtype_check(const char* name) {
|
||||
DtypeCheck get_dtype_check(const char *name) {
|
||||
py::module_ np = py::module_::import("numpy");
|
||||
DtypeCheck check{};
|
||||
check.numpy = np.attr("dtype")(np.attr(name));
|
||||
@ -31,17 +31,15 @@ DtypeCheck get_dtype_check(const char* name) {
|
||||
}
|
||||
|
||||
std::vector<DtypeCheck> get_concrete_dtype_checks() {
|
||||
return {
|
||||
// Normalization
|
||||
get_dtype_check<std::int8_t>("int8"),
|
||||
get_dtype_check<std::uint8_t>("uint8"),
|
||||
get_dtype_check<std::int16_t>("int16"),
|
||||
get_dtype_check<std::uint16_t>("uint16"),
|
||||
get_dtype_check<std::int32_t>("int32"),
|
||||
get_dtype_check<std::uint32_t>("uint32"),
|
||||
get_dtype_check<std::int64_t>("int64"),
|
||||
get_dtype_check<std::uint64_t>("uint64")
|
||||
};
|
||||
return {// Normalization
|
||||
get_dtype_check<std::int8_t>("int8"),
|
||||
get_dtype_check<std::uint8_t>("uint8"),
|
||||
get_dtype_check<std::int16_t>("int16"),
|
||||
get_dtype_check<std::uint16_t>("uint16"),
|
||||
get_dtype_check<std::int32_t>("int32"),
|
||||
get_dtype_check<std::uint32_t>("uint32"),
|
||||
get_dtype_check<std::int64_t>("int64"),
|
||||
get_dtype_check<std::uint64_t>("uint64")};
|
||||
}
|
||||
|
||||
struct DtypeSizeCheck {
|
||||
@ -80,15 +78,18 @@ using arr = py::array;
|
||||
using arr_t = py::array_t<uint16_t, 0>;
|
||||
static_assert(std::is_same<arr_t::value_type, uint16_t>::value, "");
|
||||
|
||||
template<typename... Ix> arr data(const arr& a, Ix... index) {
|
||||
template <typename... Ix>
|
||||
arr data(const arr &a, Ix... index) {
|
||||
return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...));
|
||||
}
|
||||
|
||||
template<typename... Ix> arr data_t(const arr_t& a, Ix... index) {
|
||||
template <typename... Ix>
|
||||
arr data_t(const arr_t &a, Ix... index) {
|
||||
return arr(a.size() - a.index_at(index...), a.data(index...));
|
||||
}
|
||||
|
||||
template<typename... Ix> arr& mutate_data(arr& a, Ix... index) {
|
||||
template <typename... Ix>
|
||||
arr &mutate_data(arr &a, Ix... index) {
|
||||
auto *ptr = (uint8_t *) a.mutable_data(index...);
|
||||
for (py::ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) {
|
||||
ptr[i] = (uint8_t) (ptr[i] * 2);
|
||||
@ -96,7 +97,8 @@ template<typename... Ix> arr& mutate_data(arr& a, Ix... index) {
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename... Ix> arr_t& mutate_data_t(arr_t& a, Ix... index) {
|
||||
template <typename... Ix>
|
||||
arr_t &mutate_data_t(arr_t &a, Ix... index) {
|
||||
auto ptr = a.mutable_data(index...);
|
||||
for (py::ssize_t i = 0; i < a.size() - a.index_at(index...); i++) {
|
||||
ptr[i]++;
|
||||
@ -104,20 +106,40 @@ template<typename... Ix> arr_t& mutate_data_t(arr_t& a, Ix... index) {
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename... Ix> py::ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); }
|
||||
template<typename... Ix> py::ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); }
|
||||
template<typename... Ix> py::ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); }
|
||||
template<typename... Ix> py::ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); }
|
||||
template<typename... Ix> py::ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); }
|
||||
template<typename... Ix> arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; }
|
||||
template <typename... Ix>
|
||||
py::ssize_t index_at(const arr &a, Ix... idx) {
|
||||
return a.index_at(idx...);
|
||||
}
|
||||
template <typename... Ix>
|
||||
py::ssize_t index_at_t(const arr_t &a, Ix... idx) {
|
||||
return a.index_at(idx...);
|
||||
}
|
||||
template <typename... Ix>
|
||||
py::ssize_t offset_at(const arr &a, Ix... idx) {
|
||||
return a.offset_at(idx...);
|
||||
}
|
||||
template <typename... Ix>
|
||||
py::ssize_t offset_at_t(const arr_t &a, Ix... idx) {
|
||||
return a.offset_at(idx...);
|
||||
}
|
||||
template <typename... Ix>
|
||||
py::ssize_t at_t(const arr_t &a, Ix... idx) {
|
||||
return a.at(idx...);
|
||||
}
|
||||
template <typename... Ix>
|
||||
arr_t &mutate_at_t(arr_t &a, Ix... idx) {
|
||||
a.mutable_at(idx...)++;
|
||||
return a;
|
||||
}
|
||||
|
||||
#define def_index_fn(name, type) \
|
||||
sm.def(#name, [](type a) { return name(a); }); \
|
||||
sm.def(#name, [](type a, int i) { return name(a, i); }); \
|
||||
sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \
|
||||
#define def_index_fn(name, type) \
|
||||
sm.def(#name, [](type a) { return name(a); }); \
|
||||
sm.def(#name, [](type a, int i) { return name(a, i); }); \
|
||||
sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \
|
||||
sm.def(#name, [](type a, int i, int j, int k) { return name(a, i, j, k); });
|
||||
|
||||
template <typename T, typename T2> py::handle auxiliaries(T &&r, T2 &&r2) {
|
||||
template <typename T, typename T2>
|
||||
py::handle auxiliaries(T &&r, T2 &&r2) {
|
||||
if (r.ndim() != 2) {
|
||||
throw std::domain_error("error: ndim != 2");
|
||||
}
|
||||
@ -138,16 +160,18 @@ template <typename T, typename T2> py::handle auxiliaries(T &&r, T2 &&r2) {
|
||||
static int data_i = 42;
|
||||
|
||||
TEST_SUBMODULE(numpy_array, sm) {
|
||||
try { py::module_::import("numpy"); }
|
||||
catch (...) { return; }
|
||||
try {
|
||||
py::module_::import("numpy");
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
|
||||
// test_dtypes
|
||||
py::class_<DtypeCheck>(sm, "DtypeCheck")
|
||||
.def_readonly("numpy", &DtypeCheck::numpy)
|
||||
.def_readonly("pybind11", &DtypeCheck::pybind11)
|
||||
.def("__repr__", [](const DtypeCheck& self) {
|
||||
return py::str("<DtypeCheck numpy={} pybind11={}>").format(
|
||||
self.numpy, self.pybind11);
|
||||
.def("__repr__", [](const DtypeCheck &self) {
|
||||
return py::str("<DtypeCheck numpy={} pybind11={}>").format(self.numpy, self.pybind11);
|
||||
});
|
||||
sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks);
|
||||
|
||||
@ -155,41 +179,41 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
.def_readonly("name", &DtypeSizeCheck::name)
|
||||
.def_readonly("size_cpp", &DtypeSizeCheck::size_cpp)
|
||||
.def_readonly("size_numpy", &DtypeSizeCheck::size_numpy)
|
||||
.def("__repr__", [](const DtypeSizeCheck& self) {
|
||||
return py::str("<DtypeSizeCheck name='{}' size_cpp={} size_numpy={} dtype={}>").format(
|
||||
self.name, self.size_cpp, self.size_numpy, self.dtype);
|
||||
.def("__repr__", [](const DtypeSizeCheck &self) {
|
||||
return py::str("<DtypeSizeCheck name='{}' size_cpp={} size_numpy={} dtype={}>")
|
||||
.format(self.name, self.size_cpp, self.size_numpy, self.dtype);
|
||||
});
|
||||
sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks);
|
||||
|
||||
// test_array_attributes
|
||||
sm.def("ndim", [](const arr& a) { return a.ndim(); });
|
||||
sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); });
|
||||
sm.def("shape", [](const arr& a, py::ssize_t dim) { return a.shape(dim); });
|
||||
sm.def("strides", [](const arr& a) { return arr(a.ndim(), a.strides()); });
|
||||
sm.def("strides", [](const arr& a, py::ssize_t dim) { return a.strides(dim); });
|
||||
sm.def("writeable", [](const arr& a) { return a.writeable(); });
|
||||
sm.def("size", [](const arr& a) { return a.size(); });
|
||||
sm.def("itemsize", [](const arr& a) { return a.itemsize(); });
|
||||
sm.def("nbytes", [](const arr& a) { return a.nbytes(); });
|
||||
sm.def("owndata", [](const arr& a) { return a.owndata(); });
|
||||
sm.def("ndim", [](const arr &a) { return a.ndim(); });
|
||||
sm.def("shape", [](const arr &a) { return arr(a.ndim(), a.shape()); });
|
||||
sm.def("shape", [](const arr &a, py::ssize_t dim) { return a.shape(dim); });
|
||||
sm.def("strides", [](const arr &a) { return arr(a.ndim(), a.strides()); });
|
||||
sm.def("strides", [](const arr &a, py::ssize_t dim) { return a.strides(dim); });
|
||||
sm.def("writeable", [](const arr &a) { return a.writeable(); });
|
||||
sm.def("size", [](const arr &a) { return a.size(); });
|
||||
sm.def("itemsize", [](const arr &a) { return a.itemsize(); });
|
||||
sm.def("nbytes", [](const arr &a) { return a.nbytes(); });
|
||||
sm.def("owndata", [](const arr &a) { return a.owndata(); });
|
||||
|
||||
// test_index_offset
|
||||
def_index_fn(index_at, const arr&);
|
||||
def_index_fn(index_at_t, const arr_t&);
|
||||
def_index_fn(offset_at, const arr&);
|
||||
def_index_fn(offset_at_t, const arr_t&);
|
||||
def_index_fn(index_at, const arr &);
|
||||
def_index_fn(index_at_t, const arr_t &);
|
||||
def_index_fn(offset_at, const arr &);
|
||||
def_index_fn(offset_at_t, const arr_t &);
|
||||
// test_data
|
||||
def_index_fn(data, const arr&);
|
||||
def_index_fn(data_t, const arr_t&);
|
||||
def_index_fn(data, const arr &);
|
||||
def_index_fn(data_t, const arr_t &);
|
||||
// test_mutate_data, test_mutate_readonly
|
||||
def_index_fn(mutate_data, arr&);
|
||||
def_index_fn(mutate_data_t, arr_t&);
|
||||
def_index_fn(at_t, const arr_t&);
|
||||
def_index_fn(mutate_at_t, arr_t&);
|
||||
def_index_fn(mutate_data, arr &);
|
||||
def_index_fn(mutate_data_t, arr_t &);
|
||||
def_index_fn(at_t, const arr_t &);
|
||||
def_index_fn(mutate_at_t, arr_t &);
|
||||
|
||||
// test_make_c_f_array
|
||||
sm.def("make_f_array", [] { return py::array_t<float>({ 2, 2 }, { 4, 8 }); });
|
||||
sm.def("make_c_array", [] { return py::array_t<float>({ 2, 2 }, { 8, 4 }); });
|
||||
sm.def("make_f_array", [] { return py::array_t<float>({2, 2}, {4, 8}); });
|
||||
sm.def("make_c_array", [] { return py::array_t<float>({2, 2}, {8, 4}); });
|
||||
|
||||
// test_empty_shaped_array
|
||||
sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); });
|
||||
@ -198,18 +222,16 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
|
||||
// test_wrap
|
||||
sm.def("wrap", [](const py::array &a) {
|
||||
return py::array(
|
||||
a.dtype(),
|
||||
{a.shape(), a.shape() + a.ndim()},
|
||||
{a.strides(), a.strides() + a.ndim()},
|
||||
a.data(),
|
||||
a
|
||||
);
|
||||
return py::array(a.dtype(),
|
||||
{a.shape(), a.shape() + a.ndim()},
|
||||
{a.strides(), a.strides() + a.ndim()},
|
||||
a.data(),
|
||||
a);
|
||||
});
|
||||
|
||||
// test_numpy_view
|
||||
struct ArrayClass {
|
||||
int data[2] = { 1, 2 };
|
||||
int data[2] = {1, 2};
|
||||
ArrayClass() { py::print("ArrayClass()"); }
|
||||
~ArrayClass() { py::print("~ArrayClass()"); }
|
||||
};
|
||||
@ -217,13 +239,12 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
.def(py::init<>())
|
||||
.def("numpy_view", [](py::object &obj) {
|
||||
py::print("ArrayClass::numpy_view()");
|
||||
auto &a = obj.cast<ArrayClass&>();
|
||||
auto &a = obj.cast<ArrayClass &>();
|
||||
return py::array_t<int>({2}, {4}, a.data, obj);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// test_cast_numpy_int64_to_uint64
|
||||
sm.def("function_taking_uint64", [](uint64_t) { });
|
||||
sm.def("function_taking_uint64", [](uint64_t) {});
|
||||
|
||||
// test_isinstance
|
||||
sm.def("isinstance_untyped", [](py::object yes, py::object no) {
|
||||
@ -236,18 +257,14 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
|
||||
// test_constructors
|
||||
sm.def("default_constructors", []() {
|
||||
return py::dict(
|
||||
"array"_a=py::array(),
|
||||
"array_t<int32>"_a=py::array_t<std::int32_t>(),
|
||||
"array_t<double>"_a=py::array_t<double>()
|
||||
);
|
||||
return py::dict("array"_a = py::array(),
|
||||
"array_t<int32>"_a = py::array_t<std::int32_t>(),
|
||||
"array_t<double>"_a = py::array_t<double>());
|
||||
});
|
||||
sm.def("converting_constructors", [](const py::object &o) {
|
||||
return py::dict(
|
||||
"array"_a=py::array(o),
|
||||
"array_t<int32>"_a=py::array_t<std::int32_t>(o),
|
||||
"array_t<double>"_a=py::array_t<double>(o)
|
||||
);
|
||||
return py::dict("array"_a = py::array(o),
|
||||
"array_t<int32>"_a = py::array_t<std::int32_t>(o),
|
||||
"array_t<double>"_a = py::array_t<double>(o));
|
||||
});
|
||||
|
||||
// test_overload_resolution
|
||||
@ -294,17 +311,21 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
sm.def("issue685", [](const py::object &) { return "other"; });
|
||||
|
||||
// test_array_unchecked_fixed_dims
|
||||
sm.def("proxy_add2", [](py::array_t<double> a, double v) {
|
||||
auto r = a.mutable_unchecked<2>();
|
||||
for (py::ssize_t i = 0; i < r.shape(0); i++) {
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++) {
|
||||
r(i, j) += v;
|
||||
sm.def(
|
||||
"proxy_add2",
|
||||
[](py::array_t<double> a, double v) {
|
||||
auto r = a.mutable_unchecked<2>();
|
||||
for (py::ssize_t i = 0; i < r.shape(0); i++) {
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++) {
|
||||
r(i, j) += v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, py::arg{}.noconvert(), py::arg());
|
||||
},
|
||||
py::arg{}.noconvert(),
|
||||
py::arg());
|
||||
|
||||
sm.def("proxy_init3", [](double start) {
|
||||
py::array_t<double, py::array::c_style> a({ 3, 3, 3 });
|
||||
py::array_t<double, py::array::c_style> a({3, 3, 3});
|
||||
auto r = a.mutable_unchecked<3>();
|
||||
for (py::ssize_t i = 0; i < r.shape(0); i++) {
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++) {
|
||||
@ -316,7 +337,7 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
return a;
|
||||
});
|
||||
sm.def("proxy_init3F", [](double start) {
|
||||
py::array_t<double, py::array::f_style> a({ 3, 3, 3 });
|
||||
py::array_t<double, py::array::f_style> a({3, 3, 3});
|
||||
auto r = a.mutable_unchecked<3>();
|
||||
for (py::ssize_t k = 0; k < r.shape(2); k++) {
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++) {
|
||||
@ -356,19 +377,23 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
|
||||
// test_array_unchecked_dyn_dims
|
||||
// Same as the above, but without a compile-time dimensions specification:
|
||||
sm.def("proxy_add2_dyn", [](py::array_t<double> a, double v) {
|
||||
auto r = a.mutable_unchecked();
|
||||
if (r.ndim() != 2) {
|
||||
throw std::domain_error("error: ndim != 2");
|
||||
}
|
||||
for (py::ssize_t i = 0; i < r.shape(0); i++) {
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++) {
|
||||
r(i, j) += v;
|
||||
sm.def(
|
||||
"proxy_add2_dyn",
|
||||
[](py::array_t<double> a, double v) {
|
||||
auto r = a.mutable_unchecked();
|
||||
if (r.ndim() != 2) {
|
||||
throw std::domain_error("error: ndim != 2");
|
||||
}
|
||||
}
|
||||
}, py::arg{}.noconvert(), py::arg());
|
||||
for (py::ssize_t i = 0; i < r.shape(0); i++) {
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++) {
|
||||
r(i, j) += v;
|
||||
}
|
||||
}
|
||||
},
|
||||
py::arg{}.noconvert(),
|
||||
py::arg());
|
||||
sm.def("proxy_init3_dyn", [](double start) {
|
||||
py::array_t<double, py::array::c_style> a({ 3, 3, 3 });
|
||||
py::array_t<double, py::array::c_style> a({3, 3, 3});
|
||||
auto r = a.mutable_unchecked();
|
||||
if (r.ndim() != 3) {
|
||||
throw std::domain_error("error: ndim != 3");
|
||||
@ -386,29 +411,31 @@ TEST_SUBMODULE(numpy_array, sm) {
|
||||
return auxiliaries(a.unchecked(), a.mutable_unchecked());
|
||||
});
|
||||
|
||||
sm.def("array_auxiliaries2", [](py::array_t<double> a) {
|
||||
return auxiliaries(a, a);
|
||||
});
|
||||
sm.def("array_auxiliaries2", [](py::array_t<double> a) { return auxiliaries(a, a); });
|
||||
|
||||
// test_array_failures
|
||||
// Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object:
|
||||
// Issue #785: Uninformative "Unknown internal error" exception when constructing array from
|
||||
// empty object:
|
||||
sm.def("array_fail_test", []() { return py::array(py::object()); });
|
||||
sm.def("array_t_fail_test", []() { return py::array_t<double>(py::object()); });
|
||||
// Make sure the error from numpy is being passed through:
|
||||
sm.def("array_fail_test_negative_size", []() { int c = 0; return py::array(-1, &c); });
|
||||
sm.def("array_fail_test_negative_size", []() {
|
||||
int c = 0;
|
||||
return py::array(-1, &c);
|
||||
});
|
||||
|
||||
// test_initializer_list
|
||||
// Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous
|
||||
sm.def("array_initializer_list1", []() { return py::array_t<float>(1); });
|
||||
// { 1 } also works for the above, but clang warns about it
|
||||
sm.def("array_initializer_list2", []() { return py::array_t<float>({ 1, 2 }); });
|
||||
sm.def("array_initializer_list3", []() { return py::array_t<float>({ 1, 2, 3 }); });
|
||||
sm.def("array_initializer_list4", []() { return py::array_t<float>({ 1, 2, 3, 4 }); });
|
||||
sm.def("array_initializer_list2", []() { return py::array_t<float>({1, 2}); });
|
||||
sm.def("array_initializer_list3", []() { return py::array_t<float>({1, 2, 3}); });
|
||||
sm.def("array_initializer_list4", []() { return py::array_t<float>({1, 2, 3, 4}); });
|
||||
|
||||
// test_array_resize
|
||||
// reshape array to 2D without changing size
|
||||
sm.def("array_reshape2", [](py::array_t<double> a) {
|
||||
const auto dim_sz = (py::ssize_t)std::sqrt(a.size());
|
||||
const auto dim_sz = (py::ssize_t) std::sqrt(a.size());
|
||||
if (dim_sz * dim_sz != a.size()) {
|
||||
throw std::domain_error(
|
||||
"array_reshape2: input array total size is not a squared integer");
|
||||
|
||||
@ -7,13 +7,14 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/numpy.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PYBIND11_PACKED(cls) cls __attribute__((__packed__))
|
||||
# define PYBIND11_PACKED(cls) cls __attribute__((__packed__))
|
||||
#else
|
||||
#define PYBIND11_PACKED(cls) __pragma(pack(push, 1)) cls __pragma(pack(pop))
|
||||
# define PYBIND11_PACKED(cls) __pragma(pack(push, 1)) cls __pragma(pack(pop))
|
||||
#endif
|
||||
|
||||
namespace py = pybind11;
|
||||
@ -25,7 +26,7 @@ struct SimpleStruct {
|
||||
long double ldbl_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const SimpleStruct& v) {
|
||||
std::ostream &operator<<(std::ostream &os, const SimpleStruct &v) {
|
||||
return os << "s:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
|
||||
}
|
||||
|
||||
@ -43,7 +44,7 @@ PYBIND11_PACKED(struct PackedStruct {
|
||||
long double ldbl_;
|
||||
});
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const PackedStruct& v) {
|
||||
std::ostream &operator<<(std::ostream &os, const PackedStruct &v) {
|
||||
return os << "p:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
|
||||
}
|
||||
|
||||
@ -52,7 +53,7 @@ PYBIND11_PACKED(struct NestedStruct {
|
||||
PackedStruct b;
|
||||
});
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const NestedStruct& v) {
|
||||
std::ostream &operator<<(std::ostream &os, const NestedStruct &v) {
|
||||
return os << "n:a=" << v.a << ";b=" << v.b;
|
||||
}
|
||||
|
||||
@ -70,7 +71,7 @@ struct PartialNestedStruct {
|
||||
uint64_t dummy2;
|
||||
};
|
||||
|
||||
struct UnboundStruct { };
|
||||
struct UnboundStruct {};
|
||||
|
||||
struct StringStruct {
|
||||
char a[3];
|
||||
@ -82,7 +83,7 @@ struct ComplexStruct {
|
||||
std::complex<double> cdbl;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const ComplexStruct& v) {
|
||||
std::ostream &operator<<(std::ostream &os, const ComplexStruct &v) {
|
||||
return os << "c:" << v.cflt << "," << v.cdbl;
|
||||
}
|
||||
|
||||
@ -106,7 +107,7 @@ PYBIND11_PACKED(struct EnumStruct {
|
||||
E2 e2;
|
||||
});
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const StringStruct& v) {
|
||||
std::ostream &operator<<(std::ostream &os, const StringStruct &v) {
|
||||
os << "a='";
|
||||
for (size_t i = 0; i < 3 && (v.a[i] != 0); i++) {
|
||||
os << v.a[i];
|
||||
@ -118,7 +119,7 @@ std::ostream& operator<<(std::ostream& os, const StringStruct& v) {
|
||||
return os << "'";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const ArrayStruct& v) {
|
||||
std::ostream &operator<<(std::ostream &os, const ArrayStruct &v) {
|
||||
os << "a={";
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (i > 0) {
|
||||
@ -142,15 +143,14 @@ std::ostream& operator<<(std::ostream& os, const ArrayStruct& v) {
|
||||
return os << '}';
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const EnumStruct& v) {
|
||||
std::ostream &operator<<(std::ostream &os, const EnumStruct &v) {
|
||||
return os << "e1=" << (v.e1 == E1::A ? "A" : "B") << ",e2=" << (v.e2 == E2::X ? "X" : "Y");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
py::array mkarray_via_buffer(size_t n) {
|
||||
return py::array(py::buffer_info(nullptr, sizeof(T),
|
||||
py::format_descriptor<T>::format(),
|
||||
1, { n }, { sizeof(T) }));
|
||||
return py::array(py::buffer_info(
|
||||
nullptr, sizeof(T), py::format_descriptor<T>::format(), 1, {n}, {sizeof(T)}));
|
||||
}
|
||||
|
||||
#define SET_TEST_VALS(s, i) \
|
||||
@ -188,9 +188,9 @@ py::list print_recarray(py::array_t<S, 0> arr) {
|
||||
py::array_t<int32_t, 0> test_array_ctors(int i) {
|
||||
using arr_t = py::array_t<int32_t, 0>;
|
||||
|
||||
std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 };
|
||||
std::vector<py::ssize_t> shape { 3, 2 };
|
||||
std::vector<py::ssize_t> strides { 8, 4 };
|
||||
std::vector<int32_t> data{1, 2, 3, 4, 5, 6};
|
||||
std::vector<py::ssize_t> shape{3, 2};
|
||||
std::vector<py::ssize_t> strides{8, 4};
|
||||
|
||||
auto *ptr = data.data();
|
||||
auto *vptr = (void *) ptr;
|
||||
@ -210,36 +210,62 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
|
||||
};
|
||||
|
||||
switch (i) {
|
||||
// shape: (3, 2)
|
||||
case 10: return arr_t(shape, strides, ptr);
|
||||
case 11: return py::array(shape, strides, ptr);
|
||||
case 12: return py::array(dtype, shape, strides, vptr);
|
||||
case 13: return arr_t(shape, ptr);
|
||||
case 14: return py::array(shape, ptr);
|
||||
case 15: return py::array(dtype, shape, vptr);
|
||||
case 16: return arr_t(buf_ndim2);
|
||||
case 17: return py::array(buf_ndim2);
|
||||
// shape: (3, 2) - post-fill
|
||||
case 20: return fill(arr_t(shape, strides));
|
||||
case 21: return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor
|
||||
case 22: return fill(py::array(dtype, shape, strides));
|
||||
case 23: return fill(arr_t(shape));
|
||||
case 24: return py::array(shape, ptr); // can't have nullptr due to templated ctor
|
||||
case 25: return fill(py::array(dtype, shape));
|
||||
case 26: return fill(arr_t(buf_ndim2_null));
|
||||
case 27: return fill(py::array(buf_ndim2_null));
|
||||
// shape: (6, )
|
||||
case 30: return arr_t(6, ptr);
|
||||
case 31: return py::array(6, ptr);
|
||||
case 32: return py::array(dtype, 6, vptr);
|
||||
case 33: return arr_t(buf_ndim1);
|
||||
case 34: return py::array(buf_ndim1);
|
||||
// shape: (6, )
|
||||
case 40: return fill(arr_t(6));
|
||||
case 41: return py::array(6, ptr); // can't have nullptr due to templated ctor
|
||||
case 42: return fill(py::array(dtype, 6));
|
||||
case 43: return fill(arr_t(buf_ndim1_null));
|
||||
case 44: return fill(py::array(buf_ndim1_null));
|
||||
// shape: (3, 2)
|
||||
case 10:
|
||||
return arr_t(shape, strides, ptr);
|
||||
case 11:
|
||||
return py::array(shape, strides, ptr);
|
||||
case 12:
|
||||
return py::array(dtype, shape, strides, vptr);
|
||||
case 13:
|
||||
return arr_t(shape, ptr);
|
||||
case 14:
|
||||
return py::array(shape, ptr);
|
||||
case 15:
|
||||
return py::array(dtype, shape, vptr);
|
||||
case 16:
|
||||
return arr_t(buf_ndim2);
|
||||
case 17:
|
||||
return py::array(buf_ndim2);
|
||||
// shape: (3, 2) - post-fill
|
||||
case 20:
|
||||
return fill(arr_t(shape, strides));
|
||||
case 21:
|
||||
return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor
|
||||
case 22:
|
||||
return fill(py::array(dtype, shape, strides));
|
||||
case 23:
|
||||
return fill(arr_t(shape));
|
||||
case 24:
|
||||
return py::array(shape, ptr); // can't have nullptr due to templated ctor
|
||||
case 25:
|
||||
return fill(py::array(dtype, shape));
|
||||
case 26:
|
||||
return fill(arr_t(buf_ndim2_null));
|
||||
case 27:
|
||||
return fill(py::array(buf_ndim2_null));
|
||||
// shape: (6, )
|
||||
case 30:
|
||||
return arr_t(6, ptr);
|
||||
case 31:
|
||||
return py::array(6, ptr);
|
||||
case 32:
|
||||
return py::array(dtype, 6, vptr);
|
||||
case 33:
|
||||
return arr_t(buf_ndim1);
|
||||
case 34:
|
||||
return py::array(buf_ndim1);
|
||||
// shape: (6, )
|
||||
case 40:
|
||||
return fill(arr_t(6));
|
||||
case 41:
|
||||
return py::array(6, ptr); // can't have nullptr due to templated ctor
|
||||
case 42:
|
||||
return fill(py::array(dtype, 6));
|
||||
case 43:
|
||||
return fill(arr_t(buf_ndim1_null));
|
||||
case 44:
|
||||
return fill(py::array(buf_ndim1_null));
|
||||
}
|
||||
return arr_t();
|
||||
}
|
||||
@ -251,9 +277,15 @@ py::list test_dtype_ctors() {
|
||||
list.append(py::dtype::from_args(py::str("bool")));
|
||||
py::list names, offsets, formats;
|
||||
py::dict dict;
|
||||
names.append(py::str("a")); names.append(py::str("b")); dict["names"] = names;
|
||||
offsets.append(py::int_(1)); offsets.append(py::int_(10)); dict["offsets"] = offsets;
|
||||
formats.append(py::dtype("int32")); formats.append(py::dtype("float64")); dict["formats"] = formats;
|
||||
names.append(py::str("a"));
|
||||
names.append(py::str("b"));
|
||||
dict["names"] = names;
|
||||
offsets.append(py::int_(1));
|
||||
offsets.append(py::int_(10));
|
||||
dict["offsets"] = offsets;
|
||||
formats.append(py::dtype("int32"));
|
||||
formats.append(py::dtype("float64"));
|
||||
dict["formats"] = formats;
|
||||
dict["itemsize"] = py::int_(20);
|
||||
list.append(py::dtype::from_args(dict));
|
||||
list.append(py::dtype(names, formats, offsets, 20));
|
||||
@ -266,8 +298,11 @@ struct A {};
|
||||
struct B {};
|
||||
|
||||
TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
try { py::module_::import("numpy"); }
|
||||
catch (...) { return; }
|
||||
try {
|
||||
py::module_::import("numpy");
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
|
||||
// typeinfo may be registered before the dtype descriptor for scalar casts to work...
|
||||
py::class_<SimpleStruct>(m, "SimpleStruct")
|
||||
@ -285,11 +320,10 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
if (py::len(tup) != 4) {
|
||||
throw py::cast_error("Invalid size");
|
||||
}
|
||||
return SimpleStruct{
|
||||
tup[0].cast<bool>(),
|
||||
tup[1].cast<uint32_t>(),
|
||||
tup[2].cast<float>(),
|
||||
tup[3].cast<long double>()};
|
||||
return SimpleStruct{tup[0].cast<bool>(),
|
||||
tup[1].cast<uint32_t>(),
|
||||
tup[2].cast<float>(),
|
||||
tup[3].cast<long double>()};
|
||||
});
|
||||
|
||||
PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_);
|
||||
@ -311,18 +345,18 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
#ifdef PYBIND11_NEVER_DEFINED_EVER
|
||||
// If enabled, this should produce a static_assert failure telling the user that the struct
|
||||
// is not a POD type
|
||||
struct NotPOD { std::string v; NotPOD() : v("hi") {}; };
|
||||
struct NotPOD {
|
||||
std::string v;
|
||||
NotPOD() : v("hi"){};
|
||||
};
|
||||
PYBIND11_NUMPY_DTYPE(NotPOD, v);
|
||||
#endif
|
||||
|
||||
// Check that dtypes can be registered programmatically, both from
|
||||
// initializer lists of field descriptors and from other containers.
|
||||
py::detail::npy_format_descriptor<A>::register_dtype(
|
||||
{}
|
||||
);
|
||||
py::detail::npy_format_descriptor<A>::register_dtype({});
|
||||
py::detail::npy_format_descriptor<B>::register_dtype(
|
||||
std::vector<py::detail::field_descriptor>{}
|
||||
);
|
||||
std::vector<py::detail::field_descriptor>{});
|
||||
|
||||
// test_recarray, test_scalar_conversion
|
||||
m.def("create_rec_simple", &create_recarray<SimpleStruct>);
|
||||
@ -355,17 +389,15 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
m.def("get_format_unbound", []() { return py::format_descriptor<UnboundStruct>::format(); });
|
||||
m.def("print_format_descriptors", []() {
|
||||
py::list l;
|
||||
for (const auto &fmt : {
|
||||
py::format_descriptor<SimpleStruct>::format(),
|
||||
py::format_descriptor<PackedStruct>::format(),
|
||||
py::format_descriptor<NestedStruct>::format(),
|
||||
py::format_descriptor<PartialStruct>::format(),
|
||||
py::format_descriptor<PartialNestedStruct>::format(),
|
||||
py::format_descriptor<StringStruct>::format(),
|
||||
py::format_descriptor<ArrayStruct>::format(),
|
||||
py::format_descriptor<EnumStruct>::format(),
|
||||
py::format_descriptor<ComplexStruct>::format()
|
||||
}) {
|
||||
for (const auto &fmt : {py::format_descriptor<SimpleStruct>::format(),
|
||||
py::format_descriptor<PackedStruct>::format(),
|
||||
py::format_descriptor<NestedStruct>::format(),
|
||||
py::format_descriptor<PartialStruct>::format(),
|
||||
py::format_descriptor<PartialNestedStruct>::format(),
|
||||
py::format_descriptor<StringStruct>::format(),
|
||||
py::format_descriptor<ArrayStruct>::format(),
|
||||
py::format_descriptor<EnumStruct>::format(),
|
||||
py::format_descriptor<ComplexStruct>::format()}) {
|
||||
l.append(py::cast(fmt));
|
||||
}
|
||||
return l;
|
||||
@ -373,12 +405,9 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
|
||||
// test_dtype
|
||||
std::vector<const char *> dtype_names{
|
||||
"byte", "short", "intc", "int_", "longlong",
|
||||
"ubyte", "ushort", "uintc", "uint", "ulonglong",
|
||||
"half", "single", "double", "longdouble",
|
||||
"csingle", "cdouble", "clongdouble",
|
||||
"bool_", "datetime64", "timedelta64", "object_"
|
||||
};
|
||||
"byte", "short", "intc", "int_", "longlong", "ubyte", "ushort",
|
||||
"uintc", "uint", "ulonglong", "half", "single", "double", "longdouble",
|
||||
"csingle", "cdouble", "clongdouble", "bool_", "datetime64", "timedelta64", "object_"};
|
||||
|
||||
m.def("print_dtypes", []() {
|
||||
py::list l;
|
||||
@ -415,9 +444,12 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
py::list list;
|
||||
auto dt1 = py::dtype::of<int32_t>();
|
||||
auto dt2 = py::dtype::of<SimpleStruct>();
|
||||
list.append(dt1); list.append(dt2);
|
||||
list.append(py::bool_(dt1.has_fields())); list.append(py::bool_(dt2.has_fields()));
|
||||
list.append(py::int_(dt1.itemsize())); list.append(py::int_(dt2.itemsize()));
|
||||
list.append(dt1);
|
||||
list.append(dt2);
|
||||
list.append(py::bool_(dt1.has_fields()));
|
||||
list.append(py::bool_(dt2.has_fields()));
|
||||
list.append(py::int_(dt1.itemsize()));
|
||||
list.append(py::int_(dt2.itemsize()));
|
||||
return list;
|
||||
});
|
||||
struct TrailingPaddingStruct {
|
||||
@ -436,14 +468,20 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
for (py::ssize_t i = 0; i < req.size * req.itemsize; i++) {
|
||||
static_cast<char *>(req.ptr)[i] = 0;
|
||||
}
|
||||
ptr[1].a[0] = 'a'; ptr[1].b[0] = 'a';
|
||||
ptr[2].a[0] = 'a'; ptr[2].b[0] = 'a';
|
||||
ptr[3].a[0] = 'a'; ptr[3].b[0] = 'a';
|
||||
ptr[1].a[0] = 'a';
|
||||
ptr[1].b[0] = 'a';
|
||||
ptr[2].a[0] = 'a';
|
||||
ptr[2].b[0] = 'a';
|
||||
ptr[3].a[0] = 'a';
|
||||
ptr[3].b[0] = 'a';
|
||||
|
||||
ptr[2].a[1] = 'b'; ptr[2].b[1] = 'b';
|
||||
ptr[3].a[1] = 'b'; ptr[3].b[1] = 'b';
|
||||
ptr[2].a[1] = 'b';
|
||||
ptr[2].b[1] = 'b';
|
||||
ptr[3].a[1] = 'b';
|
||||
ptr[3].b[1] = 'b';
|
||||
|
||||
ptr[3].a[2] = 'c'; ptr[3].b[2] = 'c';
|
||||
ptr[3].a[2] = 'c';
|
||||
ptr[3].b[2] = 'c';
|
||||
}
|
||||
return arr;
|
||||
});
|
||||
@ -513,14 +551,19 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
PYBIND11_NUMPY_DTYPE(CompareStruct, x, y, z);
|
||||
m.def("compare_buffer_info", []() {
|
||||
py::list list;
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<float>::compare(py::buffer_info(nullptr, sizeof(float), "f", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<unsigned>::compare(py::buffer_info(nullptr, sizeof(int), "I", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<long>::compare(py::buffer_info(nullptr, sizeof(long), "l", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<long>::compare(py::buffer_info(nullptr, sizeof(long), sizeof(long) == sizeof(int) ? "i" : "q", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<CompareStruct>::compare(py::buffer_info(nullptr, sizeof(CompareStruct), "T{?:x:3xI:y:f:z:}", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<float>::compare(
|
||||
py::buffer_info(nullptr, sizeof(float), "f", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<unsigned>::compare(
|
||||
py::buffer_info(nullptr, sizeof(int), "I", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<long>::compare(
|
||||
py::buffer_info(nullptr, sizeof(long), "l", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<long>::compare(
|
||||
py::buffer_info(nullptr, sizeof(long), sizeof(long) == sizeof(int) ? "i" : "q", 1))));
|
||||
list.append(py::bool_(py::detail::compare_buffer_info<CompareStruct>::compare(
|
||||
py::buffer_info(nullptr, sizeof(CompareStruct), "T{?:x:3xI:y:f:z:}", 1))));
|
||||
return list;
|
||||
});
|
||||
m.def("buffer_to_dtype", [](py::buffer& buf) { return py::dtype(buf.request()); });
|
||||
m.def("buffer_to_dtype", [](py::buffer &buf) { return py::dtype(buf.request()); });
|
||||
|
||||
// test_scalar_conversion
|
||||
auto f_simple = [](SimpleStruct s) { return s.uint_ * 10; };
|
||||
@ -534,7 +577,8 @@ TEST_SUBMODULE(numpy_dtypes, m) {
|
||||
m.def("f_simple_pass_thru_vectorized", py::vectorize(f_simple_pass_thru));
|
||||
|
||||
// test_register_dtype
|
||||
m.def("register_dtype", []() { PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); });
|
||||
m.def("register_dtype",
|
||||
[]() { PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); });
|
||||
|
||||
// test_str_leak
|
||||
m.def("dtype_wrapper", [](py::object d) { return py::dtype::from_args(std::move(d)); });
|
||||
|
||||
@ -8,38 +8,43 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/numpy.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
double my_func(int x, float y, double z) {
|
||||
py::print("my_func(x:int={}, y:float={:.0f}, z:float={:.0f})"_s.format(x, y, z));
|
||||
return (float) x*y*z;
|
||||
return (float) x * y * z;
|
||||
}
|
||||
|
||||
TEST_SUBMODULE(numpy_vectorize, m) {
|
||||
try { py::module_::import("numpy"); }
|
||||
catch (...) { return; }
|
||||
try {
|
||||
py::module_::import("numpy");
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
|
||||
// test_vectorize, test_docs, test_array_collapse
|
||||
// Vectorize all arguments of a function (though non-vector arguments are also allowed)
|
||||
m.def("vectorized_func", py::vectorize(my_func));
|
||||
|
||||
// Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the vectorization)
|
||||
// Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the
|
||||
// vectorization)
|
||||
m.def("vectorized_func2", [](py::array_t<int> x, py::array_t<float> y, float z) {
|
||||
return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(std::move(x),
|
||||
std::move(y));
|
||||
});
|
||||
|
||||
// Vectorize a complex-valued function
|
||||
m.def("vectorized_func3", py::vectorize(
|
||||
[](std::complex<double> c) { return c * std::complex<double>(2.f); }
|
||||
));
|
||||
m.def("vectorized_func3",
|
||||
py::vectorize([](std::complex<double> c) { return c * std::complex<double>(2.f); }));
|
||||
|
||||
// test_type_selection
|
||||
// NumPy function which only accepts specific data types
|
||||
// A lot of these no lints could be replaced with const refs, and probably should at some point.
|
||||
// A lot of these no lints could be replaced with const refs, and probably should at some
|
||||
// point.
|
||||
m.def("selective_func",
|
||||
[](const py::array_t<int, py::array::c_style> &) { return "Int branch taken."; });
|
||||
m.def("selective_func",
|
||||
@ -49,8 +54,8 @@ TEST_SUBMODULE(numpy_vectorize, m) {
|
||||
});
|
||||
|
||||
// test_passthrough_arguments
|
||||
// Passthrough test: references and non-pod types should be automatically passed through (in the
|
||||
// function definition below, only `b`, `d`, and `g` are vectorized):
|
||||
// Passthrough test: references and non-pod types should be automatically passed through (in
|
||||
// the function definition below, only `b`, `d`, and `g` are vectorized):
|
||||
struct NonPODClass {
|
||||
explicit NonPODClass(int v) : value{v} {}
|
||||
int value;
|
||||
@ -76,8 +81,7 @@ TEST_SUBMODULE(numpy_vectorize, m) {
|
||||
int value = 0;
|
||||
};
|
||||
py::class_<VectorizeTestClass> vtc(m, "VectorizeTestClass");
|
||||
vtc .def(py::init<int>())
|
||||
.def_readwrite("value", &VectorizeTestClass::value);
|
||||
vtc.def(py::init<int>()).def_readwrite("value", &VectorizeTestClass::value);
|
||||
|
||||
// Automatic vectorizing of methods
|
||||
vtc.def("method", py::vectorize(&VectorizeTestClass::method));
|
||||
@ -99,5 +103,5 @@ TEST_SUBMODULE(numpy_vectorize, m) {
|
||||
return py::detail::broadcast(buffers, ndim, shape);
|
||||
});
|
||||
|
||||
m.def("add_to", py::vectorize([](NonPODClass& x, int a) { x.value += a; }));
|
||||
m.def("add_to", py::vectorize([](NonPODClass &x, int a) { x.value += a; }));
|
||||
}
|
||||
|
||||
@ -7,8 +7,10 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
// IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures
|
||||
@ -26,12 +28,13 @@ TEST_SUBMODULE(opaque_types, m) {
|
||||
.def(py::init<>())
|
||||
.def("pop_back", &StringList::pop_back)
|
||||
/* There are multiple versions of push_back(), etc. Select the right ones. */
|
||||
.def("push_back", (void (StringList::*)(const std::string &)) &StringList::push_back)
|
||||
.def("back", (std::string &(StringList::*)()) &StringList::back)
|
||||
.def("push_back", (void(StringList::*)(const std::string &)) & StringList::push_back)
|
||||
.def("back", (std::string & (StringList::*) ()) & StringList::back)
|
||||
.def("__len__", [](const StringList &v) { return v.size(); })
|
||||
.def("__iter__", [](StringList &v) {
|
||||
return py::make_iterator(v.begin(), v.end());
|
||||
}, py::keep_alive<0, 1>());
|
||||
.def(
|
||||
"__iter__",
|
||||
[](StringList &v) { return py::make_iterator(v.begin(), v.end()); },
|
||||
py::keep_alive<0, 1>());
|
||||
|
||||
class ClassWithSTLVecProperty {
|
||||
public:
|
||||
|
||||
@ -7,12 +7,14 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
#include <functional>
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class Vector2 {
|
||||
public:
|
||||
Vector2(float x, float y) : x(x), y(y) { print_created(this, toString()); }
|
||||
@ -21,17 +23,24 @@ public:
|
||||
print_move_created(this);
|
||||
v.x = v.y = 0;
|
||||
}
|
||||
Vector2 &operator=(const Vector2 &v) { x = v.x; y = v.y; print_copy_assigned(this); return *this; }
|
||||
Vector2 &operator=(const Vector2 &v) {
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
print_copy_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator=(Vector2 &&v) noexcept {
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
v.x = v.y = 0;
|
||||
print_move_assigned(this);
|
||||
return *this;
|
||||
}
|
||||
~Vector2() { print_destroyed(this); }
|
||||
|
||||
std::string toString() const { return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; }
|
||||
std::string toString() const {
|
||||
return "[" + std::to_string(x) + ", " + std::to_string(y) + "]";
|
||||
}
|
||||
|
||||
Vector2 operator-() const { return Vector2(-x, -y); }
|
||||
Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); }
|
||||
@ -42,30 +51,51 @@ public:
|
||||
Vector2 operator/(float value) const { return Vector2(x / value, y / value); }
|
||||
Vector2 operator*(const Vector2 &v) const { return Vector2(x * v.x, y * v.y); }
|
||||
Vector2 operator/(const Vector2 &v) const { return Vector2(x / v.x, y / v.y); }
|
||||
Vector2& operator+=(const Vector2 &v) { x += v.x; y += v.y; return *this; }
|
||||
Vector2& operator-=(const Vector2 &v) { x -= v.x; y -= v.y; return *this; }
|
||||
Vector2& operator*=(float v) { x *= v; y *= v; return *this; }
|
||||
Vector2& operator/=(float v) { x /= v; y /= v; return *this; }
|
||||
Vector2& operator*=(const Vector2 &v) { x *= v.x; y *= v.y; return *this; }
|
||||
Vector2& operator/=(const Vector2 &v) { x /= v.x; y /= v.y; return *this; }
|
||||
Vector2 &operator+=(const Vector2 &v) {
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator-=(const Vector2 &v) {
|
||||
x -= v.x;
|
||||
y -= v.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator*=(float v) {
|
||||
x *= v;
|
||||
y *= v;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator/=(float v) {
|
||||
x /= v;
|
||||
y /= v;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator*=(const Vector2 &v) {
|
||||
x *= v.x;
|
||||
y *= v.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2 &operator/=(const Vector2 &v) {
|
||||
x /= v.x;
|
||||
y /= v.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend Vector2 operator+(float f, const Vector2 &v) { return Vector2(f + v.x, f + v.y); }
|
||||
friend Vector2 operator-(float f, const Vector2 &v) { return Vector2(f - v.x, f - v.y); }
|
||||
friend Vector2 operator*(float f, const Vector2 &v) { return Vector2(f * v.x, f * v.y); }
|
||||
friend Vector2 operator/(float f, const Vector2 &v) { return Vector2(f / v.x, f / v.y); }
|
||||
|
||||
bool operator==(const Vector2 &v) const {
|
||||
return x == v.x && y == v.y;
|
||||
}
|
||||
bool operator!=(const Vector2 &v) const {
|
||||
return x != v.x || y != v.y;
|
||||
}
|
||||
bool operator==(const Vector2 &v) const { return x == v.x && y == v.y; }
|
||||
bool operator!=(const Vector2 &v) const { return x != v.x || y != v.y; }
|
||||
|
||||
private:
|
||||
float x, y;
|
||||
};
|
||||
|
||||
class C1 { };
|
||||
class C2 { };
|
||||
class C1 {};
|
||||
class C2 {};
|
||||
|
||||
int operator+(const C1 &, const C1 &) { return 11; }
|
||||
int operator+(const C2 &, const C2 &) { return 22; }
|
||||
@ -84,43 +114,41 @@ bool operator==(const HashMe &lhs, const HashMe &rhs) { return lhs.member == rhs
|
||||
// namespace instead, per this recommendation:
|
||||
// https://en.cppreference.com/w/cpp/language/extending_std#Adding_template_specializations
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<Vector2> {
|
||||
// Not a good hash function, but easy to test
|
||||
size_t operator()(const Vector2 &) { return 4; }
|
||||
};
|
||||
template <>
|
||||
struct hash<Vector2> {
|
||||
// Not a good hash function, but easy to test
|
||||
size_t operator()(const Vector2 &) { return 4; }
|
||||
};
|
||||
|
||||
// HashMe has a hash function in C++ but no `__hash__` for Python.
|
||||
template <>
|
||||
struct hash<HashMe> {
|
||||
std::size_t operator()(const HashMe &selector) const {
|
||||
return std::hash<std::string>()(selector.member);
|
||||
}
|
||||
};
|
||||
// HashMe has a hash function in C++ but no `__hash__` for Python.
|
||||
template <>
|
||||
struct hash<HashMe> {
|
||||
std::size_t operator()(const HashMe &selector) const {
|
||||
return std::hash<std::string>()(selector.member);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
// Not a good abs function, but easy to test.
|
||||
std::string abs(const Vector2&) {
|
||||
return "abs(Vector2)";
|
||||
}
|
||||
std::string abs(const Vector2 &) { return "abs(Vector2)"; }
|
||||
|
||||
// MSVC & Intel warns about unknown pragmas, and warnings are errors.
|
||||
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||
#pragma GCC diagnostic push
|
||||
// clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to
|
||||
// `-Wall`, which is used here for overloading (e.g. `py::self += py::self `).
|
||||
// Here, we suppress the warning using `#pragma diagnostic`.
|
||||
// Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46
|
||||
// TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`).
|
||||
#if defined(__APPLE__) && defined(__clang__)
|
||||
#if (__clang_major__ >= 10)
|
||||
#pragma GCC diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif
|
||||
#elif defined(__clang__)
|
||||
#if (__clang_major__ >= 7)
|
||||
#pragma GCC diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif
|
||||
#endif
|
||||
# pragma GCC diagnostic push
|
||||
// clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to
|
||||
// `-Wall`, which is used here for overloading (e.g. `py::self += py::self `).
|
||||
// Here, we suppress the warning using `#pragma diagnostic`.
|
||||
// Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46
|
||||
// TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`).
|
||||
# if defined(__APPLE__) && defined(__clang__)
|
||||
# if (__clang_major__ >= 10)
|
||||
# pragma GCC diagnostic ignored "-Wself-assign-overloaded"
|
||||
# endif
|
||||
# elif defined(__clang__)
|
||||
# if (__clang_major__ >= 7)
|
||||
# pragma GCC diagnostic ignored "-Wself-assign-overloaded"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
TEST_SUBMODULE(operators, m) {
|
||||
@ -154,46 +182,52 @@ TEST_SUBMODULE(operators, m) {
|
||||
.def(py::hash(py::self))
|
||||
// N.B. See warning about usage of `py::detail::abs(py::self)` in
|
||||
// `operators.h`.
|
||||
.def("__abs__", [](const Vector2& v) { return abs(v); })
|
||||
;
|
||||
.def("__abs__", [](const Vector2 &v) { return abs(v); });
|
||||
|
||||
m.attr("Vector") = m.attr("Vector2");
|
||||
|
||||
// test_operators_notimplemented
|
||||
// #393: need to return NotSupported to ensure correct arithmetic operator behavior
|
||||
py::class_<C1>(m, "C1")
|
||||
.def(py::init<>())
|
||||
.def(py::self + py::self);
|
||||
py::class_<C1>(m, "C1").def(py::init<>()).def(py::self + py::self);
|
||||
|
||||
py::class_<C2>(m, "C2")
|
||||
.def(py::init<>())
|
||||
.def(py::self + py::self)
|
||||
.def("__add__", [](const C2& c2, const C1& c1) { return c2 + c1; })
|
||||
.def("__radd__", [](const C2& c2, const C1& c1) { return c1 + c2; });
|
||||
.def("__add__", [](const C2 &c2, const C1 &c1) { return c2 + c1; })
|
||||
.def("__radd__", [](const C2 &c2, const C1 &c1) { return c1 + c2; });
|
||||
|
||||
// test_nested
|
||||
// #328: first member in a class can't be used in operators
|
||||
struct NestABase { int value = -2; };
|
||||
struct NestABase {
|
||||
int value = -2;
|
||||
};
|
||||
py::class_<NestABase>(m, "NestABase")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("value", &NestABase::value);
|
||||
|
||||
struct NestA : NestABase {
|
||||
int value = 3;
|
||||
NestA& operator+=(int i) { value += i; return *this; }
|
||||
NestA &operator+=(int i) {
|
||||
value += i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
py::class_<NestA>(m, "NestA")
|
||||
.def(py::init<>())
|
||||
.def(py::self += int())
|
||||
.def("as_base", [](NestA &a) -> NestABase& {
|
||||
return (NestABase&) a;
|
||||
}, py::return_value_policy::reference_internal);
|
||||
.def(
|
||||
"as_base",
|
||||
[](NestA &a) -> NestABase & { return (NestABase &) a; },
|
||||
py::return_value_policy::reference_internal);
|
||||
m.def("get_NestA", [](const NestA &a) { return a.value; });
|
||||
|
||||
struct NestB {
|
||||
NestA a;
|
||||
int value = 4;
|
||||
NestB& operator-=(int i) { value -= i; return *this; }
|
||||
NestB &operator-=(int i) {
|
||||
value -= i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
py::class_<NestB>(m, "NestB")
|
||||
.def(py::init<>())
|
||||
@ -204,7 +238,10 @@ TEST_SUBMODULE(operators, m) {
|
||||
struct NestC {
|
||||
NestB b;
|
||||
int value = 5;
|
||||
NestC& operator*=(int i) { value *= i; return *this; }
|
||||
NestC &operator*=(int i) {
|
||||
value *= i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
py::class_<NestC>(m, "NestC")
|
||||
.def(py::init<>())
|
||||
@ -212,16 +249,15 @@ TEST_SUBMODULE(operators, m) {
|
||||
.def_readwrite("b", &NestC::b);
|
||||
m.def("get_NestC", [](const NestC &c) { return c.value; });
|
||||
|
||||
|
||||
// test_overriding_eq_reset_hash
|
||||
// #2191 Overriding __eq__ should set __hash__ to None
|
||||
struct Comparable {
|
||||
int value;
|
||||
bool operator==(const Comparable& rhs) const {return value == rhs.value;}
|
||||
bool operator==(const Comparable &rhs) const { return value == rhs.value; }
|
||||
};
|
||||
|
||||
struct Hashable : Comparable {
|
||||
explicit Hashable(int value): Comparable{value}{};
|
||||
explicit Hashable(int value) : Comparable{value} {};
|
||||
size_t hash() const { return static_cast<size_t>(value); }
|
||||
};
|
||||
|
||||
@ -229,9 +265,7 @@ TEST_SUBMODULE(operators, m) {
|
||||
using Hashable::Hashable;
|
||||
};
|
||||
|
||||
py::class_<Comparable>(m, "Comparable")
|
||||
.def(py::init<int>())
|
||||
.def(py::self == py::self);
|
||||
py::class_<Comparable>(m, "Comparable").def(py::init<int>()).def(py::self == py::self);
|
||||
|
||||
py::class_<Hashable>(m, "Hashable")
|
||||
.def(py::init<int>())
|
||||
@ -250,5 +284,5 @@ TEST_SUBMODULE(operators, m) {
|
||||
m.def("get_unhashable_HashMe_set", []() { return std::unordered_set<HashMe>{{"one"}}; });
|
||||
}
|
||||
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||
#pragma GCC diagnostic pop
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
@ -20,11 +20,11 @@
|
||||
namespace exercise_trampoline {
|
||||
|
||||
struct SimpleBase {
|
||||
int num = 0;
|
||||
int num = 0;
|
||||
virtual ~SimpleBase() = default;
|
||||
|
||||
// For compatibility with old clang versions:
|
||||
SimpleBase() = default;
|
||||
SimpleBase() = default;
|
||||
SimpleBase(const SimpleBase &) = default;
|
||||
};
|
||||
|
||||
@ -50,7 +50,7 @@ void wrap(py::module m) {
|
||||
}
|
||||
auto cpp_state = std::unique_ptr<SimpleBase>(new SimpleBaseTrampoline);
|
||||
cpp_state->num = t[0].cast<int>();
|
||||
auto py_state = t[1].cast<py::dict>();
|
||||
auto py_state = t[1].cast<py::dict>();
|
||||
return std::make_pair(std::move(cpp_state), py_state);
|
||||
}));
|
||||
|
||||
|
||||
@ -7,22 +7,21 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
TEST_SUBMODULE(pytypes, m) {
|
||||
// test_bool
|
||||
m.def("get_bool", []{return py::bool_(false);});
|
||||
m.def("get_bool", [] { return py::bool_(false); });
|
||||
// test_int
|
||||
m.def("get_int", []{return py::int_(0);});
|
||||
m.def("get_int", [] { return py::int_(0); });
|
||||
// test_iterator
|
||||
m.def("get_iterator", []{return py::iterator();});
|
||||
m.def("get_iterator", [] { return py::iterator(); });
|
||||
// test_iterable
|
||||
m.def("get_iterable", []{return py::iterable();});
|
||||
m.def("get_iterable", [] { return py::iterable(); });
|
||||
// test_float
|
||||
m.def("get_float", []{return py::float_(0.0f);});
|
||||
m.def("get_float", [] { return py::float_(0.0f); });
|
||||
// test_list
|
||||
m.def("list_no_args", []() { return py::list{}; });
|
||||
m.def("list_ssize_t", []() { return py::list{(py::ssize_t) 0}; });
|
||||
@ -45,7 +44,7 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
}
|
||||
});
|
||||
// test_none
|
||||
m.def("get_none", []{return py::none();});
|
||||
m.def("get_none", [] { return py::none(); });
|
||||
m.def("print_none", [](const py::none &none) { py::print("none: {}"_s.format(none)); });
|
||||
|
||||
// test_set
|
||||
@ -66,15 +65,15 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
m.def("set_contains", [](const py::set &set, const char *key) { return set.contains(key); });
|
||||
|
||||
// test_dict
|
||||
m.def("get_dict", []() { return py::dict("key"_a="value"); });
|
||||
m.def("get_dict", []() { return py::dict("key"_a = "value"); });
|
||||
m.def("print_dict", [](const py::dict &dict) {
|
||||
for (auto item : dict) {
|
||||
py::print("key: {}, value={}"_s.format(item.first, item.second));
|
||||
}
|
||||
});
|
||||
m.def("dict_keyword_constructor", []() {
|
||||
auto d1 = py::dict("x"_a=1, "y"_a=2);
|
||||
auto d2 = py::dict("z"_a=3, **d1);
|
||||
auto d1 = py::dict("x"_a = 1, "y"_a = 2);
|
||||
auto d2 = py::dict("z"_a = 3, **d1);
|
||||
return d2;
|
||||
});
|
||||
m.def("dict_contains",
|
||||
@ -91,7 +90,8 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
// test_simple_namespace
|
||||
m.def("get_simple_namespace", []() {
|
||||
auto ns = py::module_::import("types").attr("SimpleNamespace")("attr"_a=42, "x"_a="foo", "wrong"_a=1);
|
||||
auto ns = py::module_::import("types").attr("SimpleNamespace")(
|
||||
"attr"_a = 42, "x"_a = "foo", "wrong"_a = 1);
|
||||
py::delattr(ns, "wrong");
|
||||
py::setattr(ns, "right", py::int_(2));
|
||||
return ns;
|
||||
@ -103,16 +103,15 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
m.def("str_from_char_size_t", []() { return py::str{"blue", (py::size_t) 4}; });
|
||||
m.def("str_from_string", []() { return py::str(std::string("baz")); });
|
||||
m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); });
|
||||
m.def("str_from_object", [](const py::object& obj) { return py::str(obj); });
|
||||
m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); });
|
||||
m.def("str_from_object", [](const py::object &obj) { return py::str(obj); });
|
||||
m.def("repr_from_object", [](const py::object &obj) { return py::repr(obj); });
|
||||
m.def("str_from_handle", [](py::handle h) { return py::str(h); });
|
||||
m.def("str_from_string_from_str", [](const py::str& obj) {
|
||||
return py::str(static_cast<std::string>(obj));
|
||||
});
|
||||
m.def("str_from_string_from_str",
|
||||
[](const py::str &obj) { return py::str(static_cast<std::string>(obj)); });
|
||||
|
||||
m.def("str_format", []() {
|
||||
auto s1 = "{} + {} = {}"_s.format(1, 2, 3);
|
||||
auto s2 = "{a} + {b} = {c}"_s.format("a"_a=1, "b"_a=2, "c"_a=3);
|
||||
auto s2 = "{a} + {b} = {c}"_s.format("a"_a = 1, "b"_a = 2, "c"_a = 3);
|
||||
return py::make_tuple(s1, s2);
|
||||
});
|
||||
|
||||
@ -131,9 +130,7 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
// test_capsule
|
||||
m.def("return_capsule_with_destructor", []() {
|
||||
py::print("creating capsule");
|
||||
return py::capsule([]() {
|
||||
py::print("destructing capsule");
|
||||
});
|
||||
return py::capsule([]() { py::print("destructing capsule"); });
|
||||
});
|
||||
|
||||
m.def("return_capsule_with_destructor_2", []() {
|
||||
@ -148,23 +145,23 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
if (ptr) {
|
||||
const auto *name = PyCapsule_GetName(ptr);
|
||||
py::print("destructing capsule ({}, '{}')"_s.format(
|
||||
(size_t) PyCapsule_GetPointer(ptr, name), name
|
||||
));
|
||||
(size_t) PyCapsule_GetPointer(ptr, name), name));
|
||||
}
|
||||
});
|
||||
|
||||
capsule.set_pointer((void *) 1234);
|
||||
|
||||
// Using get_pointer<T>()
|
||||
void* contents1 = static_cast<void*>(capsule);
|
||||
void* contents2 = capsule.get_pointer();
|
||||
void* contents3 = capsule.get_pointer<void>();
|
||||
void *contents1 = static_cast<void *>(capsule);
|
||||
void *contents2 = capsule.get_pointer();
|
||||
void *contents3 = capsule.get_pointer<void>();
|
||||
|
||||
auto result1 = reinterpret_cast<size_t>(contents1);
|
||||
auto result2 = reinterpret_cast<size_t>(contents2);
|
||||
auto result3 = reinterpret_cast<size_t>(contents3);
|
||||
|
||||
py::print("created capsule ({}, '{}')"_s.format(result1 & result2 & result3, capsule.name()));
|
||||
py::print(
|
||||
"created capsule ({}, '{}')"_s.format(result1 & result2 & result3, capsule.name()));
|
||||
return capsule;
|
||||
});
|
||||
|
||||
@ -244,51 +241,45 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
|
||||
// test_constructors
|
||||
m.def("default_constructors", []() {
|
||||
return py::dict(
|
||||
"bytes"_a=py::bytes(),
|
||||
"bytearray"_a=py::bytearray(),
|
||||
"str"_a=py::str(),
|
||||
"bool"_a=py::bool_(),
|
||||
"int"_a=py::int_(),
|
||||
"float"_a=py::float_(),
|
||||
"tuple"_a=py::tuple(),
|
||||
"list"_a=py::list(),
|
||||
"dict"_a=py::dict(),
|
||||
"set"_a=py::set()
|
||||
);
|
||||
return py::dict("bytes"_a = py::bytes(),
|
||||
"bytearray"_a = py::bytearray(),
|
||||
"str"_a = py::str(),
|
||||
"bool"_a = py::bool_(),
|
||||
"int"_a = py::int_(),
|
||||
"float"_a = py::float_(),
|
||||
"tuple"_a = py::tuple(),
|
||||
"list"_a = py::list(),
|
||||
"dict"_a = py::dict(),
|
||||
"set"_a = py::set());
|
||||
});
|
||||
|
||||
m.def("converting_constructors", [](const py::dict &d) {
|
||||
return py::dict(
|
||||
"bytes"_a=py::bytes(d["bytes"]),
|
||||
"bytearray"_a=py::bytearray(d["bytearray"]),
|
||||
"str"_a=py::str(d["str"]),
|
||||
"bool"_a=py::bool_(d["bool"]),
|
||||
"int"_a=py::int_(d["int"]),
|
||||
"float"_a=py::float_(d["float"]),
|
||||
"tuple"_a=py::tuple(d["tuple"]),
|
||||
"list"_a=py::list(d["list"]),
|
||||
"dict"_a=py::dict(d["dict"]),
|
||||
"set"_a=py::set(d["set"]),
|
||||
"memoryview"_a=py::memoryview(d["memoryview"])
|
||||
);
|
||||
return py::dict("bytes"_a = py::bytes(d["bytes"]),
|
||||
"bytearray"_a = py::bytearray(d["bytearray"]),
|
||||
"str"_a = py::str(d["str"]),
|
||||
"bool"_a = py::bool_(d["bool"]),
|
||||
"int"_a = py::int_(d["int"]),
|
||||
"float"_a = py::float_(d["float"]),
|
||||
"tuple"_a = py::tuple(d["tuple"]),
|
||||
"list"_a = py::list(d["list"]),
|
||||
"dict"_a = py::dict(d["dict"]),
|
||||
"set"_a = py::set(d["set"]),
|
||||
"memoryview"_a = py::memoryview(d["memoryview"]));
|
||||
});
|
||||
|
||||
m.def("cast_functions", [](const py::dict &d) {
|
||||
// When converting between Python types, obj.cast<T>() should be the same as T(obj)
|
||||
return py::dict(
|
||||
"bytes"_a=d["bytes"].cast<py::bytes>(),
|
||||
"bytearray"_a=d["bytearray"].cast<py::bytearray>(),
|
||||
"str"_a=d["str"].cast<py::str>(),
|
||||
"bool"_a=d["bool"].cast<py::bool_>(),
|
||||
"int"_a=d["int"].cast<py::int_>(),
|
||||
"float"_a=d["float"].cast<py::float_>(),
|
||||
"tuple"_a=d["tuple"].cast<py::tuple>(),
|
||||
"list"_a=d["list"].cast<py::list>(),
|
||||
"dict"_a=d["dict"].cast<py::dict>(),
|
||||
"set"_a=d["set"].cast<py::set>(),
|
||||
"memoryview"_a=d["memoryview"].cast<py::memoryview>()
|
||||
);
|
||||
return py::dict("bytes"_a = d["bytes"].cast<py::bytes>(),
|
||||
"bytearray"_a = d["bytearray"].cast<py::bytearray>(),
|
||||
"str"_a = d["str"].cast<py::str>(),
|
||||
"bool"_a = d["bool"].cast<py::bool_>(),
|
||||
"int"_a = d["int"].cast<py::int_>(),
|
||||
"float"_a = d["float"].cast<py::float_>(),
|
||||
"tuple"_a = d["tuple"].cast<py::tuple>(),
|
||||
"list"_a = d["list"].cast<py::list>(),
|
||||
"dict"_a = d["dict"].cast<py::dict>(),
|
||||
"set"_a = d["set"].cast<py::set>(),
|
||||
"memoryview"_a = d["memoryview"].cast<py::memoryview>());
|
||||
});
|
||||
|
||||
m.def("convert_to_pybind11_str", [](const py::object &o) { return py::str(o); });
|
||||
@ -341,10 +332,7 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
l.append(py::cast(12));
|
||||
l.append(py::int_(15));
|
||||
|
||||
return py::dict(
|
||||
"d"_a=d,
|
||||
"l"_a=l
|
||||
);
|
||||
return py::dict("d"_a = d, "l"_a = l);
|
||||
});
|
||||
|
||||
// test_print
|
||||
@ -352,16 +340,17 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
py::print("Hello, World!");
|
||||
py::print(1, 2.0, "three", true, std::string("-- multiple args"));
|
||||
auto args = py::make_tuple("and", "a", "custom", "separator");
|
||||
py::print("*args", *args, "sep"_a="-");
|
||||
py::print("no new line here", "end"_a=" -- ");
|
||||
py::print("*args", *args, "sep"_a = "-");
|
||||
py::print("no new line here", "end"_a = " -- ");
|
||||
py::print("next print");
|
||||
|
||||
auto py_stderr = py::module_::import("sys").attr("stderr");
|
||||
py::print("this goes to stderr", "file"_a=py_stderr);
|
||||
py::print("this goes to stderr", "file"_a = py_stderr);
|
||||
|
||||
py::print("flush", "flush"_a=true);
|
||||
py::print("flush", "flush"_a = true);
|
||||
|
||||
py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this"));
|
||||
py::print(
|
||||
"{a} + {b} = {c}"_s.format("a"_a = "py::print", "b"_a = "str.format", "c"_a = "this"));
|
||||
});
|
||||
|
||||
m.def("print_failure", []() { py::print(42, UnregisteredType()); });
|
||||
@ -406,8 +395,8 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
[](const py::buffer &b) { return py::memoryview(b.request()); });
|
||||
|
||||
m.def("test_memoryview_from_buffer", [](bool is_unsigned) {
|
||||
static const int16_t si16[] = { 3, 1, 4, 1, 5 };
|
||||
static const uint16_t ui16[] = { 2, 7, 1, 8 };
|
||||
static const int16_t si16[] = {3, 1, 4, 1, 5};
|
||||
static const uint16_t ui16[] = {2, 7, 1, 8};
|
||||
if (is_unsigned) {
|
||||
return py::memoryview::from_buffer(ui16, {4}, {sizeof(uint16_t)});
|
||||
}
|
||||
@ -415,32 +404,29 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
});
|
||||
|
||||
m.def("test_memoryview_from_buffer_nativeformat", []() {
|
||||
static const char* format = "@i";
|
||||
static const int32_t arr[] = { 4, 7, 5 };
|
||||
return py::memoryview::from_buffer(
|
||||
arr, sizeof(int32_t), format, { 3 }, { sizeof(int32_t) });
|
||||
static const char *format = "@i";
|
||||
static const int32_t arr[] = {4, 7, 5};
|
||||
return py::memoryview::from_buffer(arr, sizeof(int32_t), format, {3}, {sizeof(int32_t)});
|
||||
});
|
||||
|
||||
m.def("test_memoryview_from_buffer_empty_shape", []() {
|
||||
static const char* buf = "";
|
||||
return py::memoryview::from_buffer(buf, 1, "B", { }, { });
|
||||
static const char *buf = "";
|
||||
return py::memoryview::from_buffer(buf, 1, "B", {}, {});
|
||||
});
|
||||
|
||||
m.def("test_memoryview_from_buffer_invalid_strides", []() {
|
||||
static const char* buf = "\x02\x03\x04";
|
||||
return py::memoryview::from_buffer(buf, 1, "B", { 3 }, { });
|
||||
static const char *buf = "\x02\x03\x04";
|
||||
return py::memoryview::from_buffer(buf, 1, "B", {3}, {});
|
||||
});
|
||||
|
||||
m.def("test_memoryview_from_buffer_nullptr", []() {
|
||||
return py::memoryview::from_buffer(
|
||||
static_cast<void*>(nullptr), 1, "B", { }, { });
|
||||
return py::memoryview::from_buffer(static_cast<void *>(nullptr), 1, "B", {}, {});
|
||||
});
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
m.def("test_memoryview_from_memory", []() {
|
||||
const char* buf = "\xff\xe1\xab\x37";
|
||||
return py::memoryview::from_memory(
|
||||
buf, static_cast<py::ssize_t>(strlen(buf)));
|
||||
const char *buf = "\xff\xe1\xab\x37";
|
||||
return py::memoryview::from_memory(buf, static_cast<py::ssize_t>(strlen(buf)));
|
||||
});
|
||||
#endif
|
||||
|
||||
@ -461,8 +447,7 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
m.def("pass_to_std_string", [](const std::string &s) { return s.size(); });
|
||||
|
||||
// test_weakref
|
||||
m.def("weakref_from_handle",
|
||||
[](py::handle h) { return py::weakref(h); });
|
||||
m.def("weakref_from_handle", [](py::handle h) { return py::weakref(h); });
|
||||
m.def("weakref_from_handle_and_function",
|
||||
[](py::handle h, py::function f) { return py::weakref(h, std::move(f)); });
|
||||
m.def("weakref_from_object", [](const py::object &o) { return py::weakref(o); });
|
||||
|
||||
@ -8,44 +8,52 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
#include <optional>
|
||||
#endif // PYBIND11_HAS_OPTIONAL
|
||||
# include <optional>
|
||||
#endif // PYBIND11_HAS_OPTIONAL
|
||||
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
class NonZeroIterator {
|
||||
const T* ptr_;
|
||||
const T *ptr_;
|
||||
|
||||
public:
|
||||
explicit NonZeroIterator(const T *ptr) : ptr_(ptr) {}
|
||||
const T& operator*() const { return *ptr_; }
|
||||
NonZeroIterator& operator++() { ++ptr_; return *this; }
|
||||
const T &operator*() const { return *ptr_; }
|
||||
NonZeroIterator &operator++() {
|
||||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class NonZeroSentinel {};
|
||||
|
||||
template<typename A, typename B>
|
||||
bool operator==(const NonZeroIterator<std::pair<A, B>>& it, const NonZeroSentinel&) {
|
||||
template <typename A, typename B>
|
||||
bool operator==(const NonZeroIterator<std::pair<A, B>> &it, const NonZeroSentinel &) {
|
||||
return !(*it).first || !(*it).second;
|
||||
}
|
||||
|
||||
/* Iterator where dereferencing returns prvalues instead of references. */
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
class NonRefIterator {
|
||||
const T* ptr_;
|
||||
const T *ptr_;
|
||||
|
||||
public:
|
||||
explicit NonRefIterator(const T *ptr) : ptr_(ptr) {}
|
||||
T operator*() const { return T(*ptr_); }
|
||||
NonRefIterator& operator++() { ++ptr_; return *this; }
|
||||
NonRefIterator &operator++() {
|
||||
++ptr_;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const NonRefIterator &other) const { return ptr_ == other.ptr_; }
|
||||
};
|
||||
|
||||
@ -54,17 +62,18 @@ public:
|
||||
explicit NonCopyableInt(int value) : value_(value) {}
|
||||
NonCopyableInt(const NonCopyableInt &) = delete;
|
||||
NonCopyableInt(NonCopyableInt &&other) noexcept : value_(other.value_) {
|
||||
other.value_ = -1; // detect when an unwanted move occurs
|
||||
other.value_ = -1; // detect when an unwanted move occurs
|
||||
}
|
||||
NonCopyableInt &operator=(const NonCopyableInt &) = delete;
|
||||
NonCopyableInt &operator=(NonCopyableInt &&other) noexcept {
|
||||
value_ = other.value_;
|
||||
other.value_ = -1; // detect when an unwanted move occurs
|
||||
other.value_ = -1; // detect when an unwanted move occurs
|
||||
return *this;
|
||||
}
|
||||
int get() const { return value_; }
|
||||
void set(int value) { value_ = value; }
|
||||
~NonCopyableInt() = default;
|
||||
|
||||
private:
|
||||
int value_;
|
||||
};
|
||||
@ -81,7 +90,9 @@ py::list test_random_access_iterator(PythonType x) {
|
||||
auto checks = py::list();
|
||||
auto assert_equal = [&checks](py::handle a, py::handle b) {
|
||||
auto result = PyObject_RichCompareBool(a.ptr(), b.ptr(), Py_EQ);
|
||||
if (result == -1) { throw py::error_already_set(); }
|
||||
if (result == -1) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
checks.append(result != 0);
|
||||
};
|
||||
|
||||
@ -116,7 +127,7 @@ py::list test_random_access_iterator(PythonType x) {
|
||||
|
||||
TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
// test_sliceable
|
||||
class Sliceable{
|
||||
class Sliceable {
|
||||
public:
|
||||
explicit Sliceable(int n) : size(n) {}
|
||||
int start, stop, step;
|
||||
@ -130,18 +141,20 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
int istart = static_cast<int>(start);
|
||||
int istop = static_cast<int>(stop);
|
||||
int istep = static_cast<int>(step);
|
||||
int istop = static_cast<int>(stop);
|
||||
int istep = static_cast<int>(step);
|
||||
return std::make_tuple(istart, istop, istep);
|
||||
});
|
||||
|
||||
m.def("make_forward_slice_size_t", []() { return py::slice(0, -1, 1); });
|
||||
m.def("make_reversed_slice_object", []() { return py::slice(py::none(), py::none(), py::int_(-1)); });
|
||||
m.def("make_reversed_slice_object",
|
||||
[]() { return py::slice(py::none(), py::none(), py::int_(-1)); });
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
m.attr("has_optional") = true;
|
||||
m.def("make_reversed_slice_size_t_optional_verbose", []() { return py::slice(std::nullopt, std::nullopt, -1); });
|
||||
// Warning: The following spelling may still compile if optional<> is not present and give wrong answers.
|
||||
// Please use with caution.
|
||||
m.def("make_reversed_slice_size_t_optional_verbose",
|
||||
[]() { return py::slice(std::nullopt, std::nullopt, -1); });
|
||||
// Warning: The following spelling may still compile if optional<> is not present and give
|
||||
// wrong answers. Please use with caution.
|
||||
m.def("make_reversed_slice_size_t_optional", []() { return py::slice({}, {}, -1); });
|
||||
#else
|
||||
m.attr("has_optional") = false;
|
||||
@ -166,7 +179,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
print_copy_created(this);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||
m_data = new float[m_size];
|
||||
memcpy(m_data, s.m_data, sizeof(float)*m_size);
|
||||
memcpy(m_data, s.m_data, sizeof(float) * m_size);
|
||||
}
|
||||
Sequence(Sequence &&s) noexcept : m_size(s.m_size), m_data(s.m_data) {
|
||||
print_move_created(this);
|
||||
@ -174,14 +187,17 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
s.m_data = nullptr;
|
||||
}
|
||||
|
||||
~Sequence() { print_destroyed(this); delete[] m_data; }
|
||||
~Sequence() {
|
||||
print_destroyed(this);
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
Sequence &operator=(const Sequence &s) {
|
||||
if (&s != this) {
|
||||
delete[] m_data;
|
||||
m_size = s.m_size;
|
||||
m_data = new float[m_size];
|
||||
memcpy(m_data, s.m_data, sizeof(float)*m_size);
|
||||
memcpy(m_data, s.m_data, sizeof(float) * m_size);
|
||||
}
|
||||
print_copy_assigned(this);
|
||||
return *this;
|
||||
@ -235,7 +251,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
const float *begin() const { return m_data; }
|
||||
const float *end() const { return m_data+m_size; }
|
||||
const float *end() const { return m_data + m_size; }
|
||||
|
||||
private:
|
||||
size_t m_size;
|
||||
@ -303,8 +319,8 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
;
|
||||
|
||||
// test_map_iterator
|
||||
// Interface of a map-like object that isn't (directly) an unordered_map, but provides some basic
|
||||
// map-like functionality.
|
||||
// Interface of a map-like object that isn't (directly) an unordered_map, but provides some
|
||||
// basic map-like functionality.
|
||||
class StringMap {
|
||||
public:
|
||||
StringMap() = default;
|
||||
@ -314,8 +330,10 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
void set(const std::string &key, std::string val) { map[key] = std::move(val); }
|
||||
std::string get(const std::string &key) const { return map.at(key); }
|
||||
size_t size() const { return map.size(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> map;
|
||||
|
||||
public:
|
||||
decltype(map.cbegin()) begin() const { return map.cbegin(); }
|
||||
decltype(map.cend()) end() const { return map.cend(); }
|
||||
@ -350,90 +368,115 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
class IntPairs {
|
||||
public:
|
||||
explicit IntPairs(std::vector<std::pair<int, int>> data) : data_(std::move(data)) {}
|
||||
const std::pair<int, int>* begin() const { return data_.data(); }
|
||||
const std::pair<int, int> *begin() const { return data_.data(); }
|
||||
// .end() only required for py::make_iterator(self) overload
|
||||
const std::pair<int, int>* end() const { return data_.data() + data_.size(); }
|
||||
const std::pair<int, int> *end() const { return data_.data() + data_.size(); }
|
||||
|
||||
private:
|
||||
std::vector<std::pair<int, int>> data_;
|
||||
};
|
||||
py::class_<IntPairs>(m, "IntPairs")
|
||||
.def(py::init<std::vector<std::pair<int, int>>>())
|
||||
.def("nonzero", [](const IntPairs& s) {
|
||||
return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel());
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def("nonzero_keys", [](const IntPairs& s) {
|
||||
return py::make_key_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel());
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def("nonzero_values", [](const IntPairs& s) {
|
||||
return py::make_value_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel());
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonzero",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
|
||||
NonZeroSentinel());
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonzero_keys",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_key_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
|
||||
NonZeroSentinel());
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonzero_values",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_value_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
|
||||
NonZeroSentinel());
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
|
||||
// test iterator that returns values instead of references
|
||||
.def("nonref", [](const IntPairs& s) {
|
||||
return py::make_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
|
||||
NonRefIterator<std::pair<int, int>>(s.end()));
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def("nonref_keys", [](const IntPairs& s) {
|
||||
return py::make_key_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
|
||||
NonRefIterator<std::pair<int, int>>(s.end()));
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def("nonref_values", [](const IntPairs& s) {
|
||||
return py::make_value_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
|
||||
NonRefIterator<std::pair<int, int>>(s.end()));
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonref",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
|
||||
NonRefIterator<std::pair<int, int>>(s.end()));
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonref_keys",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_key_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
|
||||
NonRefIterator<std::pair<int, int>>(s.end()));
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"nonref_values",
|
||||
[](const IntPairs &s) {
|
||||
return py::make_value_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
|
||||
NonRefIterator<std::pair<int, int>>(s.end()));
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
|
||||
// test single-argument make_iterator
|
||||
.def("simple_iterator", [](IntPairs& self) {
|
||||
return py::make_iterator(self);
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def("simple_keys", [](IntPairs& self) {
|
||||
return py::make_key_iterator(self);
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def("simple_values", [](IntPairs& self) {
|
||||
return py::make_value_iterator(self);
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"simple_iterator",
|
||||
[](IntPairs &self) { return py::make_iterator(self); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"simple_keys",
|
||||
[](IntPairs &self) { return py::make_key_iterator(self); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"simple_values",
|
||||
[](IntPairs &self) { return py::make_value_iterator(self); },
|
||||
py::keep_alive<0, 1>())
|
||||
|
||||
// Test iterator with an Extra (doesn't do anything useful, so not used
|
||||
// at runtime, but tests need to be able to compile with the correct
|
||||
// overload. See PR #3293.
|
||||
.def("_make_iterator_extras", [](IntPairs& self) {
|
||||
return py::make_iterator(self, py::call_guard<int>());
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def("_make_key_extras", [](IntPairs& self) {
|
||||
return py::make_key_iterator(self, py::call_guard<int>());
|
||||
}, py::keep_alive<0, 1>())
|
||||
.def("_make_value_extras", [](IntPairs& self) {
|
||||
return py::make_value_iterator(self, py::call_guard<int>());
|
||||
}, py::keep_alive<0, 1>())
|
||||
;
|
||||
.def(
|
||||
"_make_iterator_extras",
|
||||
[](IntPairs &self) { return py::make_iterator(self, py::call_guard<int>()); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"_make_key_extras",
|
||||
[](IntPairs &self) { return py::make_key_iterator(self, py::call_guard<int>()); },
|
||||
py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"_make_value_extras",
|
||||
[](IntPairs &self) { return py::make_value_iterator(self, py::call_guard<int>()); },
|
||||
py::keep_alive<0, 1>());
|
||||
|
||||
// test_iterater_referencing
|
||||
py::class_<NonCopyableInt>(m, "NonCopyableInt")
|
||||
.def(py::init<int>())
|
||||
.def("set", &NonCopyableInt::set)
|
||||
.def("__int__", &NonCopyableInt::get)
|
||||
;
|
||||
.def("__int__", &NonCopyableInt::get);
|
||||
py::class_<std::vector<NonCopyableInt>>(m, "VectorNonCopyableInt")
|
||||
.def(py::init<>())
|
||||
.def("append", [](std::vector<NonCopyableInt> &vec, int value) {
|
||||
vec.emplace_back(value);
|
||||
})
|
||||
.def("append",
|
||||
[](std::vector<NonCopyableInt> &vec, int value) { vec.emplace_back(value); })
|
||||
.def("__iter__", [](std::vector<NonCopyableInt> &vec) {
|
||||
return py::make_iterator(vec.begin(), vec.end());
|
||||
})
|
||||
;
|
||||
});
|
||||
py::class_<std::vector<NonCopyableIntPair>>(m, "VectorNonCopyableIntPair")
|
||||
.def(py::init<>())
|
||||
.def("append", [](std::vector<NonCopyableIntPair> &vec, const std::pair<int, int> &value) {
|
||||
vec.emplace_back(NonCopyableInt(value.first), NonCopyableInt(value.second));
|
||||
})
|
||||
.def("keys", [](std::vector<NonCopyableIntPair> &vec) {
|
||||
return py::make_key_iterator(vec.begin(), vec.end());
|
||||
})
|
||||
.def("append",
|
||||
[](std::vector<NonCopyableIntPair> &vec, const std::pair<int, int> &value) {
|
||||
vec.emplace_back(NonCopyableInt(value.first), NonCopyableInt(value.second));
|
||||
})
|
||||
.def("keys",
|
||||
[](std::vector<NonCopyableIntPair> &vec) {
|
||||
return py::make_key_iterator(vec.begin(), vec.end());
|
||||
})
|
||||
.def("values", [](std::vector<NonCopyableIntPair> &vec) {
|
||||
return py::make_value_iterator(vec.begin(), vec.end());
|
||||
})
|
||||
;
|
||||
});
|
||||
|
||||
#if 0
|
||||
// Obsolete: special data structure for exposing custom iterator types to python
|
||||
@ -511,7 +554,9 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
|
||||
|
||||
// test_iterator_rvp
|
||||
// #388: Can't make iterators via make_iterator() with different r/v policies
|
||||
static std::vector<int> list = { 1, 2, 3 };
|
||||
m.def("make_iterator_1", []() { return py::make_iterator<py::return_value_policy::copy>(list); });
|
||||
m.def("make_iterator_2", []() { return py::make_iterator<py::return_value_policy::automatic>(list); });
|
||||
static std::vector<int> list = {1, 2, 3};
|
||||
m.def("make_iterator_1",
|
||||
[]() { return py::make_iterator<py::return_value_policy::copy>(list); });
|
||||
m.def("make_iterator_2",
|
||||
[]() { return py::make_iterator<py::return_value_policy::automatic>(list); });
|
||||
}
|
||||
|
||||
@ -8,21 +8,23 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC
|
||||
# pragma warning(disable: 4702) // unreachable code in system header (xatomic.h(382))
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC
|
||||
# pragma warning(disable : 4702) // unreachable code in system header (xatomic.h(382))
|
||||
#endif
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "object.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the
|
||||
// holder size to trigger the non-simple-layout internal instance layout for single inheritance with
|
||||
// large holder type:
|
||||
template <typename T> class huge_unique_ptr {
|
||||
// holder size to trigger the non-simple-layout internal instance layout for single inheritance
|
||||
// with large holder type:
|
||||
template <typename T>
|
||||
class huge_unique_ptr {
|
||||
std::unique_ptr<T> ptr;
|
||||
uint64_t padding[10];
|
||||
|
||||
public:
|
||||
explicit huge_unique_ptr(T *p) : ptr(p) {}
|
||||
T *get() { return ptr.get(); }
|
||||
@ -32,10 +34,11 @@ public:
|
||||
template <typename T>
|
||||
class custom_unique_ptr {
|
||||
std::unique_ptr<T> impl;
|
||||
|
||||
public:
|
||||
explicit custom_unique_ptr(T *p) : impl(p) {}
|
||||
T* get() const { return impl.get(); }
|
||||
T* release_ptr() { return impl.release(); }
|
||||
T *get() const { return impl.get(); }
|
||||
T *release_ptr() { return impl.release(); }
|
||||
};
|
||||
|
||||
// Simple custom holder that works like shared_ptr and has operator& overload
|
||||
@ -44,11 +47,12 @@ public:
|
||||
template <typename T>
|
||||
class shared_ptr_with_addressof_operator {
|
||||
std::shared_ptr<T> impl;
|
||||
|
||||
public:
|
||||
shared_ptr_with_addressof_operator( ) = default;
|
||||
shared_ptr_with_addressof_operator() = default;
|
||||
explicit shared_ptr_with_addressof_operator(T *p) : impl(p) {}
|
||||
T* get() const { return impl.get(); }
|
||||
T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); }
|
||||
T *get() const { return impl.get(); }
|
||||
T **operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); }
|
||||
};
|
||||
|
||||
// Simple custom holder that works like unique_ptr and has operator& overload
|
||||
@ -57,12 +61,13 @@ public:
|
||||
template <typename T>
|
||||
class unique_ptr_with_addressof_operator {
|
||||
std::unique_ptr<T> impl;
|
||||
|
||||
public:
|
||||
unique_ptr_with_addressof_operator() = default;
|
||||
explicit unique_ptr_with_addressof_operator(T *p) : impl(p) {}
|
||||
T* get() const { return impl.get(); }
|
||||
T* release_ptr() { return impl.release(); }
|
||||
T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); }
|
||||
T *get() const { return impl.get(); }
|
||||
T *release_ptr() { return impl.release(); }
|
||||
T **operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); }
|
||||
};
|
||||
|
||||
// Custom object with builtin reference counting (see 'object.h' for the implementation)
|
||||
@ -70,8 +75,10 @@ class MyObject1 : public Object {
|
||||
public:
|
||||
explicit MyObject1(int value) : value(value) { print_created(this, toString()); }
|
||||
std::string toString() const override { return "MyObject1[" + std::to_string(value) + "]"; }
|
||||
|
||||
protected:
|
||||
~MyObject1() override { print_destroyed(this); }
|
||||
|
||||
private:
|
||||
int value;
|
||||
};
|
||||
@ -83,6 +90,7 @@ public:
|
||||
explicit MyObject2(int value) : value(value) { print_created(this, toString()); }
|
||||
std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; }
|
||||
virtual ~MyObject2() { print_destroyed(this); }
|
||||
|
||||
private:
|
||||
int value;
|
||||
};
|
||||
@ -94,6 +102,7 @@ public:
|
||||
explicit MyObject3(int value) : value(value) { print_created(this, toString()); }
|
||||
std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; }
|
||||
virtual ~MyObject3() { print_destroyed(this); }
|
||||
|
||||
private:
|
||||
int value;
|
||||
};
|
||||
@ -117,6 +126,7 @@ public:
|
||||
delete o;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
~MyObject4() {
|
||||
myobject4_instances.erase(this);
|
||||
@ -144,6 +154,7 @@ public:
|
||||
delete o;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~MyObject4a() {
|
||||
myobject4a_instances.erase(this);
|
||||
@ -232,14 +243,14 @@ struct TypeForMoveOnlyHolderWithAddressOf {
|
||||
};
|
||||
|
||||
// test_smart_ptr_from_default
|
||||
struct HeldByDefaultHolder { };
|
||||
struct HeldByDefaultHolder {};
|
||||
|
||||
// test_shared_ptr_gc
|
||||
// #187: issue involving std::shared_ptr<> return value policy & garbage collection
|
||||
struct ElementBase {
|
||||
virtual ~ElementBase() = default; /* Force creation of virtual table */
|
||||
ElementBase() = default;
|
||||
ElementBase(const ElementBase&) = delete;
|
||||
ElementBase(const ElementBase &) = delete;
|
||||
};
|
||||
|
||||
struct ElementA : ElementBase {
|
||||
@ -259,11 +270,12 @@ struct ElementList {
|
||||
// It is always possible to construct a ref<T> from an Object* pointer without
|
||||
// possible inconsistencies, hence the 'true' argument at the end.
|
||||
// Make pybind11 aware of the non-standard getter member function
|
||||
namespace pybind11 { namespace detail {
|
||||
template <typename T>
|
||||
struct holder_helper<ref<T>> {
|
||||
static const T *get(const ref<T> &p) { return p.get_ptr(); }
|
||||
};
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct holder_helper<ref<T>> {
|
||||
static const T *get(const ref<T> &p) { return p.get_ptr(); }
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
|
||||
@ -287,8 +299,7 @@ TEST_SUBMODULE(smart_ptr, m) {
|
||||
py::class_<Object, ref<Object>> obj(m, "Object");
|
||||
obj.def("getRefCount", &Object::getRefCount);
|
||||
|
||||
py::class_<MyObject1, ref<MyObject1>>(m, "MyObject1", obj)
|
||||
.def(py::init<int>());
|
||||
py::class_<MyObject1, ref<MyObject1>>(m, "MyObject1", obj).def(py::init<int>());
|
||||
py::implicitly_convertible<py::int_, MyObject1>();
|
||||
|
||||
m.def("make_object_1", []() -> Object * { return new MyObject1(1); });
|
||||
@ -307,25 +318,27 @@ TEST_SUBMODULE(smart_ptr, m) {
|
||||
// Expose constructor stats for the ref type
|
||||
m.def("cstats_ref", &ConstructorStats::get<ref_tag>);
|
||||
|
||||
py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2")
|
||||
.def(py::init<int>());
|
||||
py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2").def(py::init<int>());
|
||||
m.def("make_myobject2_1", []() { return new MyObject2(6); });
|
||||
m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); });
|
||||
m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); });
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
m.def("print_myobject2_2", [](std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); });
|
||||
m.def("print_myobject2_3", [](const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); });
|
||||
m.def("print_myobject2_4", [](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); });
|
||||
m.def("print_myobject2_3",
|
||||
[](const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); });
|
||||
m.def("print_myobject2_4",
|
||||
[](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); });
|
||||
|
||||
py::class_<MyObject3, std::shared_ptr<MyObject3>>(m, "MyObject3")
|
||||
.def(py::init<int>());
|
||||
py::class_<MyObject3, std::shared_ptr<MyObject3>>(m, "MyObject3").def(py::init<int>());
|
||||
m.def("make_myobject3_1", []() { return new MyObject3(8); });
|
||||
m.def("make_myobject3_2", []() { return std::make_shared<MyObject3>(9); });
|
||||
m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); });
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
m.def("print_myobject3_2", [](std::shared_ptr<MyObject3> obj) { py::print(obj->toString()); });
|
||||
m.def("print_myobject3_3", [](const std::shared_ptr<MyObject3> &obj) { py::print(obj->toString()); });
|
||||
m.def("print_myobject3_4", [](const std::shared_ptr<MyObject3> *obj) { py::print((*obj)->toString()); });
|
||||
m.def("print_myobject3_3",
|
||||
[](const std::shared_ptr<MyObject3> &obj) { py::print(obj->toString()); });
|
||||
m.def("print_myobject3_4",
|
||||
[](const std::shared_ptr<MyObject3> *obj) { py::print((*obj)->toString()); });
|
||||
|
||||
// test_smart_ptr_refcounting
|
||||
m.def("test_object1_refcounting", []() {
|
||||
@ -421,11 +434,18 @@ TEST_SUBMODULE(smart_ptr, m) {
|
||||
[](const HolderWithAddressOf *obj) { py::print((*obj).get()->toString()); });
|
||||
|
||||
// test_move_only_holder_with_addressof_operator
|
||||
using MoveOnlyHolderWithAddressOf = unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>;
|
||||
py::class_<TypeForMoveOnlyHolderWithAddressOf, MoveOnlyHolderWithAddressOf>(m, "TypeForMoveOnlyHolderWithAddressOf")
|
||||
.def_static("make", []() { return MoveOnlyHolderWithAddressOf(new TypeForMoveOnlyHolderWithAddressOf(0)); })
|
||||
using MoveOnlyHolderWithAddressOf
|
||||
= unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>;
|
||||
py::class_<TypeForMoveOnlyHolderWithAddressOf, MoveOnlyHolderWithAddressOf>(
|
||||
m, "TypeForMoveOnlyHolderWithAddressOf")
|
||||
.def_static("make",
|
||||
[]() {
|
||||
return MoveOnlyHolderWithAddressOf(
|
||||
new TypeForMoveOnlyHolderWithAddressOf(0));
|
||||
})
|
||||
.def_readwrite("value", &TypeForMoveOnlyHolderWithAddressOf::value)
|
||||
.def("print_object", [](const TypeForMoveOnlyHolderWithAddressOf *obj) { py::print(obj->toString()); });
|
||||
.def("print_object",
|
||||
[](const TypeForMoveOnlyHolderWithAddressOf *obj) { py::print(obj->toString()); });
|
||||
|
||||
// test_smart_ptr_from_default
|
||||
py::class_<HeldByDefaultHolder, std::unique_ptr<HeldByDefaultHolder>>(m, "HeldByDefaultHolder")
|
||||
|
||||
@ -7,39 +7,43 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#ifndef PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL
|
||||
#define PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL
|
||||
# define PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL
|
||||
#endif
|
||||
#include <pybind11/stl/filesystem.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(PYBIND11_TEST_BOOST)
|
||||
#include <boost/optional.hpp>
|
||||
# include <boost/optional.hpp>
|
||||
|
||||
namespace pybind11 { namespace detail {
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {};
|
||||
|
||||
template <>
|
||||
struct type_caster<boost::none_t> : void_caster<boost::none_t> {};
|
||||
}} // namespace pybind11::detail
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
#endif
|
||||
|
||||
// Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14
|
||||
#if defined(PYBIND11_HAS_VARIANT)
|
||||
using std::variant;
|
||||
#elif defined(PYBIND11_TEST_BOOST) && (!defined(_MSC_VER) || _MSC_VER >= 1910)
|
||||
# include <boost/variant.hpp>
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
# include <boost/variant.hpp>
|
||||
# define PYBIND11_HAS_VARIANT 1
|
||||
using boost::variant;
|
||||
|
||||
namespace pybind11 { namespace detail {
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <typename... Ts>
|
||||
struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};
|
||||
|
||||
@ -50,7 +54,8 @@ struct visit_helper<boost::variant> {
|
||||
return boost::apply_visitor(args...);
|
||||
}
|
||||
};
|
||||
}} // namespace pybind11::detail
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
#endif
|
||||
|
||||
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
|
||||
@ -63,26 +68,23 @@ struct TplCtorClass {
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<TplCtorClass> { size_t operator()(const TplCtorClass &) const { return 0; } };
|
||||
template <>
|
||||
struct hash<TplCtorClass> {
|
||||
size_t operator()(const TplCtorClass &) const { return 0; }
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
|
||||
template <template <typename> class OptionalImpl, typename T>
|
||||
struct OptionalHolder
|
||||
{
|
||||
struct OptionalHolder {
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default): breaks GCC 4.8
|
||||
OptionalHolder() {};
|
||||
bool member_initialized() const {
|
||||
return member && member->initialized;
|
||||
}
|
||||
OptionalHolder(){};
|
||||
bool member_initialized() const { return member && member->initialized; }
|
||||
OptionalImpl<T> member = T{};
|
||||
};
|
||||
|
||||
|
||||
enum class EnumType {
|
||||
kSet = 42,
|
||||
kUnset = 85,
|
||||
kSet = 42,
|
||||
kUnset = 85,
|
||||
};
|
||||
|
||||
// This is used to test that return-by-ref and return-by-copy policies are
|
||||
@ -102,7 +104,7 @@ public:
|
||||
value = EnumType::kUnset;
|
||||
}
|
||||
|
||||
OptionalEnumValue& access_by_ref() { return value; }
|
||||
OptionalEnumValue &access_by_ref() { return value; }
|
||||
OptionalEnumValue access_by_copy() { return value; }
|
||||
|
||||
private:
|
||||
@ -122,62 +124,56 @@ public:
|
||||
|
||||
ReferenceSensitiveOptional() = default;
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
ReferenceSensitiveOptional(const T& value) : storage{value} {}
|
||||
ReferenceSensitiveOptional(const T &value) : storage{value} {}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
ReferenceSensitiveOptional(T&& value) : storage{std::move(value)} {}
|
||||
ReferenceSensitiveOptional& operator=(const T& value) {
|
||||
ReferenceSensitiveOptional(T &&value) : storage{std::move(value)} {}
|
||||
ReferenceSensitiveOptional &operator=(const T &value) {
|
||||
storage = {value};
|
||||
return *this;
|
||||
}
|
||||
ReferenceSensitiveOptional& operator=(T&& value) {
|
||||
ReferenceSensitiveOptional &operator=(T &&value) {
|
||||
storage = {std::move(value)};
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
T& emplace(Args&&... args) {
|
||||
T &emplace(Args &&...args) {
|
||||
storage.clear();
|
||||
storage.emplace_back(std::forward<Args>(args)...);
|
||||
return storage.back();
|
||||
}
|
||||
|
||||
const T& value() const noexcept {
|
||||
const T &value() const noexcept {
|
||||
assert(!storage.empty());
|
||||
return storage[0];
|
||||
}
|
||||
|
||||
const T& operator*() const noexcept {
|
||||
return value();
|
||||
}
|
||||
const T &operator*() const noexcept { return value(); }
|
||||
|
||||
const T* operator->() const noexcept {
|
||||
return &value();
|
||||
}
|
||||
const T *operator->() const noexcept { return &value(); }
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return !storage.empty();
|
||||
}
|
||||
explicit operator bool() const noexcept { return !storage.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<T> storage;
|
||||
};
|
||||
|
||||
namespace pybind11 { namespace detail {
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct type_caster<ReferenceSensitiveOptional<T>> : optional_caster<ReferenceSensitiveOptional<T>> {};
|
||||
struct type_caster<ReferenceSensitiveOptional<T>>
|
||||
: optional_caster<ReferenceSensitiveOptional<T>> {};
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
|
||||
|
||||
TEST_SUBMODULE(stl, m) {
|
||||
// test_vector
|
||||
m.def("cast_vector", []() { return std::vector<int>{1}; });
|
||||
m.def("load_vector", [](const std::vector<int> &v) { return v.at(0) == 1 && v.at(1) == 2; });
|
||||
// `std::vector<bool>` is special because it returns proxy objects instead of references
|
||||
m.def("cast_bool_vector", []() { return std::vector<bool>{true, false}; });
|
||||
m.def("load_bool_vector", [](const std::vector<bool> &v) {
|
||||
return v.at(0) == true && v.at(1) == false;
|
||||
});
|
||||
m.def("load_bool_vector",
|
||||
[](const std::vector<bool> &v) { return v.at(0) == true && v.at(1) == false; });
|
||||
// Unnumbered regression (caused by #936): pointers to stl containers aren't castable
|
||||
static std::vector<RValueCaster> lvv{2};
|
||||
m.def("cast_ptr_vector", []() { return &lvv; });
|
||||
@ -187,12 +183,12 @@ TEST_SUBMODULE(stl, m) {
|
||||
m.def("load_deque", [](const std::deque<int> &v) { return v.at(0) == 1 && v.at(1) == 2; });
|
||||
|
||||
// test_array
|
||||
m.def("cast_array", []() { return std::array<int, 2> {{1 , 2}}; });
|
||||
m.def("cast_array", []() { return std::array<int, 2>{{1, 2}}; });
|
||||
m.def("load_array", [](const std::array<int, 2> &a) { return a[0] == 1 && a[1] == 2; });
|
||||
|
||||
// test_valarray
|
||||
m.def("cast_valarray", []() { return std::valarray<int>{1, 4, 9}; });
|
||||
m.def("load_valarray", [](const std::valarray<int>& v) {
|
||||
m.def("load_valarray", [](const std::valarray<int> &v) {
|
||||
return v.size() == 3 && v[0] == 1 && v[1] == 4 && v[2] == 9;
|
||||
});
|
||||
|
||||
@ -214,10 +210,12 @@ TEST_SUBMODULE(stl, m) {
|
||||
// NB: map and set keys are `const`, so while we technically do move them (as `const Type &&`),
|
||||
// casters don't typically do anything with that, which means they fall to the `const Type &`
|
||||
// caster.
|
||||
m.def("cast_rv_map", []() { return std::unordered_map<std::string, RValueCaster>{{"a", RValueCaster{}}}; });
|
||||
m.def("cast_rv_map", []() {
|
||||
return std::unordered_map<std::string, RValueCaster>{{"a", RValueCaster{}}};
|
||||
});
|
||||
m.def("cast_rv_nested", []() {
|
||||
std::vector<std::array<std::list<std::unordered_map<std::string, RValueCaster>>, 2>> v;
|
||||
v.emplace_back(); // add an array
|
||||
v.emplace_back(); // add an array
|
||||
v.back()[0].emplace_back(); // add a map to the array
|
||||
v.back()[0].back().emplace("b", RValueCaster{});
|
||||
v.back()[0].back().emplace("c", RValueCaster{});
|
||||
@ -226,13 +224,15 @@ TEST_SUBMODULE(stl, m) {
|
||||
return v;
|
||||
});
|
||||
static std::array<RValueCaster, 2> lva;
|
||||
static std::unordered_map<std::string, RValueCaster> lvm{{"a", RValueCaster{}}, {"b", RValueCaster{}}};
|
||||
static std::unordered_map<std::string, std::vector<std::list<std::array<RValueCaster, 2>>>> lvn;
|
||||
lvn["a"].emplace_back(); // add a list
|
||||
static std::unordered_map<std::string, RValueCaster> lvm{{"a", RValueCaster{}},
|
||||
{"b", RValueCaster{}}};
|
||||
static std::unordered_map<std::string, std::vector<std::list<std::array<RValueCaster, 2>>>>
|
||||
lvn;
|
||||
lvn["a"].emplace_back(); // add a list
|
||||
lvn["a"].back().emplace_back(); // add an array
|
||||
lvn["a"].emplace_back(); // another list
|
||||
lvn["a"].emplace_back(); // another list
|
||||
lvn["a"].back().emplace_back(); // add an array
|
||||
lvn["b"].emplace_back(); // add a list
|
||||
lvn["b"].emplace_back(); // add a list
|
||||
lvn["b"].back().emplace_back(); // add an array
|
||||
lvn["b"].back().emplace_back(); // add another array
|
||||
m.def("cast_lv_vector", []() -> const decltype(lvv) & { return lvv; });
|
||||
@ -253,7 +253,9 @@ TEST_SUBMODULE(stl, m) {
|
||||
|
||||
// test_move_out_container
|
||||
struct MoveOutContainer {
|
||||
struct Value { int value; };
|
||||
struct Value {
|
||||
int value;
|
||||
};
|
||||
std::list<Value> move_list() const { return {{0}, {1}, {2}}; }
|
||||
};
|
||||
py::class_<MoveOutContainer::Value>(m, "MoveOutContainerValue")
|
||||
@ -266,7 +268,7 @@ TEST_SUBMODULE(stl, m) {
|
||||
struct NoAssign {
|
||||
int value;
|
||||
|
||||
explicit NoAssign(int value = 0) : value(value) { }
|
||||
explicit NoAssign(int value = 0) : value(value) {}
|
||||
NoAssign(const NoAssign &) = default;
|
||||
NoAssign(NoAssign &&) = default;
|
||||
|
||||
@ -277,13 +279,10 @@ TEST_SUBMODULE(stl, m) {
|
||||
.def(py::init<>())
|
||||
.def(py::init<int>());
|
||||
|
||||
|
||||
struct MoveOutDetector
|
||||
{
|
||||
struct MoveOutDetector {
|
||||
MoveOutDetector() = default;
|
||||
MoveOutDetector(const MoveOutDetector&) = default;
|
||||
MoveOutDetector(MoveOutDetector&& other) noexcept
|
||||
: initialized(other.initialized) {
|
||||
MoveOutDetector(const MoveOutDetector &) = default;
|
||||
MoveOutDetector(MoveOutDetector &&other) noexcept : initialized(other.initialized) {
|
||||
// steal underlying resource
|
||||
other.initialized = false;
|
||||
}
|
||||
@ -293,23 +292,22 @@ TEST_SUBMODULE(stl, m) {
|
||||
.def(py::init<>())
|
||||
.def_readonly("initialized", &MoveOutDetector::initialized);
|
||||
|
||||
|
||||
#ifdef PYBIND11_HAS_OPTIONAL
|
||||
// test_optional
|
||||
m.attr("has_optional") = true;
|
||||
|
||||
using opt_int = std::optional<int>;
|
||||
using opt_no_assign = std::optional<NoAssign>;
|
||||
m.def("double_or_zero", [](const opt_int& x) -> int {
|
||||
return x.value_or(0) * 2;
|
||||
});
|
||||
m.def("double_or_zero", [](const opt_int &x) -> int { return x.value_or(0) * 2; });
|
||||
m.def("half_or_none", [](int x) -> opt_int { return x != 0 ? opt_int(x / 2) : opt_int(); });
|
||||
m.def("test_nullopt", [](opt_int x) {
|
||||
return x.value_or(42);
|
||||
}, py::arg_v("x", std::nullopt, "None"));
|
||||
m.def("test_no_assign", [](const opt_no_assign &x) {
|
||||
return x ? x->value : 42;
|
||||
}, py::arg_v("x", std::nullopt, "None"));
|
||||
m.def(
|
||||
"test_nullopt",
|
||||
[](opt_int x) { return x.value_or(42); },
|
||||
py::arg_v("x", std::nullopt, "None"));
|
||||
m.def(
|
||||
"test_no_assign",
|
||||
[](const opt_no_assign &x) { return x ? x->value : 42; },
|
||||
py::arg_v("x", std::nullopt, "None"));
|
||||
|
||||
m.def("nodefer_none_optional", [](std::optional<int>) { return true; });
|
||||
m.def("nodefer_none_optional", [](const py::none &) { return false; });
|
||||
@ -333,18 +331,17 @@ TEST_SUBMODULE(stl, m) {
|
||||
|
||||
using exp_opt_int = std::experimental::optional<int>;
|
||||
using exp_opt_no_assign = std::experimental::optional<NoAssign>;
|
||||
m.def("double_or_zero_exp", [](const exp_opt_int& x) -> int {
|
||||
return x.value_or(0) * 2;
|
||||
});
|
||||
m.def("half_or_none_exp", [](int x) -> exp_opt_int {
|
||||
return x ? exp_opt_int(x / 2) : exp_opt_int();
|
||||
});
|
||||
m.def("test_nullopt_exp", [](exp_opt_int x) {
|
||||
return x.value_or(42);
|
||||
}, py::arg_v("x", std::experimental::nullopt, "None"));
|
||||
m.def("test_no_assign_exp", [](const exp_opt_no_assign &x) {
|
||||
return x ? x->value : 42;
|
||||
}, py::arg_v("x", std::experimental::nullopt, "None"));
|
||||
m.def("double_or_zero_exp", [](const exp_opt_int &x) -> int { return x.value_or(0) * 2; });
|
||||
m.def("half_or_none_exp",
|
||||
[](int x) -> exp_opt_int { return x ? exp_opt_int(x / 2) : exp_opt_int(); });
|
||||
m.def(
|
||||
"test_nullopt_exp",
|
||||
[](exp_opt_int x) { return x.value_or(42); },
|
||||
py::arg_v("x", std::experimental::nullopt, "None"));
|
||||
m.def(
|
||||
"test_no_assign_exp",
|
||||
[](const exp_opt_no_assign &x) { return x ? x->value : 42; },
|
||||
py::arg_v("x", std::experimental::nullopt, "None"));
|
||||
|
||||
using opt_exp_holder = OptionalHolder<std::experimental::optional, MoveOutDetector>;
|
||||
py::class_<opt_exp_holder>(m, "OptionalExpHolder", "Class with optional member")
|
||||
@ -365,18 +362,17 @@ TEST_SUBMODULE(stl, m) {
|
||||
|
||||
using boost_opt_int = boost::optional<int>;
|
||||
using boost_opt_no_assign = boost::optional<NoAssign>;
|
||||
m.def("double_or_zero_boost", [](const boost_opt_int& x) -> int {
|
||||
return x.value_or(0) * 2;
|
||||
});
|
||||
m.def("half_or_none_boost", [](int x) -> boost_opt_int {
|
||||
return x != 0 ? boost_opt_int(x / 2) : boost_opt_int();
|
||||
});
|
||||
m.def("test_nullopt_boost", [](boost_opt_int x) {
|
||||
return x.value_or(42);
|
||||
}, py::arg_v("x", boost::none, "None"));
|
||||
m.def("test_no_assign_boost", [](const boost_opt_no_assign &x) {
|
||||
return x ? x->value : 42;
|
||||
}, py::arg_v("x", boost::none, "None"));
|
||||
m.def("double_or_zero_boost", [](const boost_opt_int &x) -> int { return x.value_or(0) * 2; });
|
||||
m.def("half_or_none_boost",
|
||||
[](int x) -> boost_opt_int { return x != 0 ? boost_opt_int(x / 2) : boost_opt_int(); });
|
||||
m.def(
|
||||
"test_nullopt_boost",
|
||||
[](boost_opt_int x) { return x.value_or(42); },
|
||||
py::arg_v("x", boost::none, "None"));
|
||||
m.def(
|
||||
"test_no_assign_boost",
|
||||
[](const boost_opt_no_assign &x) { return x ? x->value : 42; },
|
||||
py::arg_v("x", boost::none, "None"));
|
||||
|
||||
using opt_boost_holder = OptionalHolder<boost::optional, MoveOutDetector>;
|
||||
py::class_<opt_boost_holder>(m, "OptionalBoostHolder", "Class with optional member")
|
||||
@ -394,9 +390,8 @@ TEST_SUBMODULE(stl, m) {
|
||||
// test_refsensitive_optional
|
||||
using refsensitive_opt_int = ReferenceSensitiveOptional<int>;
|
||||
using refsensitive_opt_no_assign = ReferenceSensitiveOptional<NoAssign>;
|
||||
m.def("double_or_zero_refsensitive", [](const refsensitive_opt_int& x) -> int {
|
||||
return (x ? x.value() : 0) * 2;
|
||||
});
|
||||
m.def("double_or_zero_refsensitive",
|
||||
[](const refsensitive_opt_int &x) -> int { return (x ? x.value() : 0) * 2; });
|
||||
m.def("half_or_none_refsensitive", [](int x) -> refsensitive_opt_int {
|
||||
return x != 0 ? refsensitive_opt_int(x / 2) : refsensitive_opt_int();
|
||||
});
|
||||
@ -405,12 +400,14 @@ TEST_SUBMODULE(stl, m) {
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
[](refsensitive_opt_int x) { return x ? x.value() : 42; },
|
||||
py::arg_v("x", refsensitive_opt_int(), "None"));
|
||||
m.def("test_no_assign_refsensitive", [](const refsensitive_opt_no_assign &x) {
|
||||
return x ? x->value : 42;
|
||||
}, py::arg_v("x", refsensitive_opt_no_assign(), "None"));
|
||||
m.def(
|
||||
"test_no_assign_refsensitive",
|
||||
[](const refsensitive_opt_no_assign &x) { return x ? x->value : 42; },
|
||||
py::arg_v("x", refsensitive_opt_no_assign(), "None"));
|
||||
|
||||
using opt_refsensitive_holder = OptionalHolder<ReferenceSensitiveOptional, MoveOutDetector>;
|
||||
py::class_<opt_refsensitive_holder>(m, "OptionalRefSensitiveHolder", "Class with optional member")
|
||||
py::class_<opt_refsensitive_holder>(
|
||||
m, "OptionalRefSensitiveHolder", "Class with optional member")
|
||||
.def(py::init<>())
|
||||
.def_readonly("member", &opt_refsensitive_holder::member)
|
||||
.def("member_initialized", &opt_refsensitive_holder::member_initialized);
|
||||
@ -424,7 +421,7 @@ TEST_SUBMODULE(stl, m) {
|
||||
#ifdef PYBIND11_HAS_FILESYSTEM
|
||||
// test_fs_path
|
||||
m.attr("has_filesystem") = true;
|
||||
m.def("parent_path", [](const std::filesystem::path& p) { return p.parent_path(); });
|
||||
m.def("parent_path", [](const std::filesystem::path &p) { return p.parent_path(); });
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_HAS_VARIANT
|
||||
@ -472,13 +469,13 @@ TEST_SUBMODULE(stl, m) {
|
||||
// #171: Can't return STL structures containing reference wrapper
|
||||
m.def("return_vec_of_reference_wrapper", [](std::reference_wrapper<UserType> p4) {
|
||||
static UserType p1{1}, p2{2}, p3{3};
|
||||
return std::vector<std::reference_wrapper<UserType>> {
|
||||
std::ref(p1), std::ref(p2), std::ref(p3), p4
|
||||
};
|
||||
return std::vector<std::reference_wrapper<UserType>>{
|
||||
std::ref(p1), std::ref(p2), std::ref(p3), p4};
|
||||
});
|
||||
|
||||
// test_stl_pass_by_pointer
|
||||
m.def("stl_pass_by_pointer", [](std::vector<int>* v) { return *v; }, "v"_a=nullptr);
|
||||
m.def(
|
||||
"stl_pass_by_pointer", [](std::vector<int> *v) { return *v; }, "v"_a = nullptr);
|
||||
|
||||
// #1258: pybind11/stl.h converts string to vector<string>
|
||||
m.def("func_with_string_or_vector_string_arg_overload",
|
||||
@ -496,19 +493,24 @@ TEST_SUBMODULE(stl, m) {
|
||||
py::class_<Placeholder>(m, "Placeholder");
|
||||
|
||||
/// test_stl_vector_ownership
|
||||
m.def("test_stl_ownership",
|
||||
[]() {
|
||||
std::vector<Placeholder *> result;
|
||||
result.push_back(new Placeholder());
|
||||
return result;
|
||||
},
|
||||
py::return_value_policy::take_ownership);
|
||||
m.def(
|
||||
"test_stl_ownership",
|
||||
[]() {
|
||||
std::vector<Placeholder *> result;
|
||||
result.push_back(new Placeholder());
|
||||
return result;
|
||||
},
|
||||
py::return_value_policy::take_ownership);
|
||||
|
||||
m.def("array_cast_sequence", [](std::array<int, 3> x) { return x; });
|
||||
|
||||
/// test_issue_1561
|
||||
struct Issue1561Inner { std::string data; };
|
||||
struct Issue1561Outer { std::vector<Issue1561Inner> list; };
|
||||
struct Issue1561Inner {
|
||||
std::string data;
|
||||
};
|
||||
struct Issue1561Outer {
|
||||
std::vector<Issue1561Inner> list;
|
||||
};
|
||||
|
||||
py::class_<Issue1561Inner>(m, "Issue1561Inner")
|
||||
.def(py::init<std::string>())
|
||||
|
||||
@ -7,12 +7,13 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <pybind11/numpy.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <pybind11/stl_bind.h>
|
||||
#include <pybind11/numpy.h>
|
||||
#include <map>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
class El {
|
||||
@ -23,7 +24,7 @@ public:
|
||||
int a;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream &s, El const&v) {
|
||||
std::ostream &operator<<(std::ostream &s, El const &v) {
|
||||
s << "El{" << v.a << '}';
|
||||
return s;
|
||||
}
|
||||
@ -40,7 +41,8 @@ public:
|
||||
int value;
|
||||
};
|
||||
|
||||
template <class Container> Container *one_to_n(int n) {
|
||||
template <class Container>
|
||||
Container *one_to_n(int n) {
|
||||
auto *v = new Container();
|
||||
for (int i = 1; i <= n; i++) {
|
||||
v->emplace_back(i);
|
||||
@ -48,7 +50,8 @@ template <class Container> Container *one_to_n(int n) {
|
||||
return v;
|
||||
}
|
||||
|
||||
template <class Map> Map *times_ten(int n) {
|
||||
template <class Map>
|
||||
Map *times_ten(int n) {
|
||||
auto *m = new Map();
|
||||
for (int i = 1; i <= n; i++) {
|
||||
m->emplace(int(i), E_nc(10 * i));
|
||||
@ -56,7 +59,8 @@ template <class Map> Map *times_ten(int n) {
|
||||
return m;
|
||||
}
|
||||
|
||||
template <class NestMap> NestMap *times_hundred(int n) {
|
||||
template <class NestMap>
|
||||
NestMap *times_hundred(int n) {
|
||||
auto *m = new NestMap();
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++) {
|
||||
@ -71,8 +75,7 @@ TEST_SUBMODULE(stl_binders, m) {
|
||||
py::bind_vector<std::vector<unsigned int>>(m, "VectorInt", py::buffer_protocol());
|
||||
|
||||
// test_vector_custom
|
||||
py::class_<El>(m, "El")
|
||||
.def(py::init<int>());
|
||||
py::class_<El>(m, "El").def(py::init<int>());
|
||||
py::bind_vector<std::vector<El>>(m, "VectorEl");
|
||||
py::bind_vector<std::vector<std::vector<El>>>(m, "VectorVectorEl");
|
||||
|
||||
@ -82,11 +85,10 @@ TEST_SUBMODULE(stl_binders, m) {
|
||||
|
||||
// test_map_string_double_const
|
||||
py::bind_map<std::map<std::string, double const>>(m, "MapStringDoubleConst");
|
||||
py::bind_map<std::unordered_map<std::string, double const>>(m, "UnorderedMapStringDoubleConst");
|
||||
py::bind_map<std::unordered_map<std::string, double const>>(m,
|
||||
"UnorderedMapStringDoubleConst");
|
||||
|
||||
py::class_<E_nc>(m, "ENC")
|
||||
.def(py::init<int>())
|
||||
.def_readwrite("value", &E_nc::value);
|
||||
py::class_<E_nc>(m, "ENC").def(py::init<int>()).def_readwrite("value", &E_nc::value);
|
||||
|
||||
// test_noncopyable_containers
|
||||
py::bind_vector<std::vector<E_nc>>(m, "VectorENC");
|
||||
@ -116,17 +118,31 @@ TEST_SUBMODULE(stl_binders, m) {
|
||||
// test_vector_buffer
|
||||
py::bind_vector<std::vector<unsigned char>>(m, "VectorUChar", py::buffer_protocol());
|
||||
// no dtype declared for this version:
|
||||
struct VUndeclStruct { bool w; uint32_t x; double y; bool z; };
|
||||
m.def("create_undeclstruct", [m] () mutable {
|
||||
py::bind_vector<std::vector<VUndeclStruct>>(m, "VectorUndeclStruct", py::buffer_protocol());
|
||||
struct VUndeclStruct {
|
||||
bool w;
|
||||
uint32_t x;
|
||||
double y;
|
||||
bool z;
|
||||
};
|
||||
m.def("create_undeclstruct", [m]() mutable {
|
||||
py::bind_vector<std::vector<VUndeclStruct>>(
|
||||
m, "VectorUndeclStruct", py::buffer_protocol());
|
||||
});
|
||||
|
||||
// The rest depends on numpy:
|
||||
try { py::module_::import("numpy"); }
|
||||
catch (...) { return; }
|
||||
try {
|
||||
py::module_::import("numpy");
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
|
||||
// test_vector_buffer_numpy
|
||||
struct VStruct { bool w; uint32_t x; double y; bool z; };
|
||||
struct VStruct {
|
||||
bool w;
|
||||
uint32_t x;
|
||||
double y;
|
||||
bool z;
|
||||
};
|
||||
PYBIND11_NUMPY_DTYPE(VStruct, w, x, y, z);
|
||||
py::class_<VStruct>(m, "VStruct").def_readwrite("x", &VStruct::x);
|
||||
py::bind_vector<std::vector<VStruct>>(m, "VectorStruct", py::buffer_protocol());
|
||||
|
||||
@ -7,11 +7,11 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
struct Animal
|
||||
{
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
struct Animal {
|
||||
// Make this type also a "standard" polymorphic type, to confirm that
|
||||
// specializing polymorphic_type_hook using enable_if_t still works
|
||||
// (https://github.com/pybind/pybind11/pull/2016/).
|
||||
@ -20,57 +20,54 @@ struct Animal
|
||||
// Enum for tag-based polymorphism.
|
||||
enum class Kind {
|
||||
Unknown = 0,
|
||||
Dog = 100, Labrador, Chihuahua, LastDog = 199,
|
||||
Cat = 200, Panther, LastCat = 299
|
||||
Dog = 100,
|
||||
Labrador,
|
||||
Chihuahua,
|
||||
LastDog = 199,
|
||||
Cat = 200,
|
||||
Panther,
|
||||
LastCat = 299
|
||||
};
|
||||
static const std::type_info* type_of_kind(Kind kind);
|
||||
static const std::type_info *type_of_kind(Kind kind);
|
||||
static std::string name_of_kind(Kind kind);
|
||||
|
||||
const Kind kind;
|
||||
const std::string name;
|
||||
|
||||
protected:
|
||||
Animal(const std::string& _name, Kind _kind)
|
||||
: kind(_kind), name(_name)
|
||||
{}
|
||||
protected:
|
||||
Animal(const std::string &_name, Kind _kind) : kind(_kind), name(_name) {}
|
||||
};
|
||||
|
||||
struct Dog : Animal
|
||||
{
|
||||
struct Dog : Animal {
|
||||
explicit Dog(const std::string &_name, Kind _kind = Kind::Dog) : Animal(_name, _kind) {}
|
||||
std::string bark() const { return name_of_kind(kind) + " " + name + " goes " + sound; }
|
||||
std::string sound = "WOOF!";
|
||||
};
|
||||
|
||||
struct Labrador : Dog
|
||||
{
|
||||
struct Labrador : Dog {
|
||||
explicit Labrador(const std::string &_name, int _excitement = 9001)
|
||||
: Dog(_name, Kind::Labrador), excitement(_excitement) {}
|
||||
int excitement;
|
||||
};
|
||||
|
||||
struct Chihuahua : Dog
|
||||
{
|
||||
struct Chihuahua : Dog {
|
||||
explicit Chihuahua(const std::string &_name) : Dog(_name, Kind::Chihuahua) {
|
||||
sound = "iyiyiyiyiyi";
|
||||
}
|
||||
std::string bark() const { return Dog::bark() + " and runs in circles"; }
|
||||
};
|
||||
|
||||
struct Cat : Animal
|
||||
{
|
||||
struct Cat : Animal {
|
||||
explicit Cat(const std::string &_name, Kind _kind = Kind::Cat) : Animal(_name, _kind) {}
|
||||
std::string purr() const { return "mrowr"; }
|
||||
};
|
||||
|
||||
struct Panther : Cat
|
||||
{
|
||||
struct Panther : Cat {
|
||||
explicit Panther(const std::string &_name) : Cat(_name, Kind::Panther) {}
|
||||
std::string purr() const { return "mrrrRRRRRR"; }
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<Animal>> create_zoo()
|
||||
{
|
||||
std::vector<std::unique_ptr<Animal>> create_zoo() {
|
||||
std::vector<std::unique_ptr<Animal>> ret;
|
||||
ret.emplace_back(new Labrador("Fido", 15000));
|
||||
|
||||
@ -85,19 +82,24 @@ std::vector<std::unique_ptr<Animal>> create_zoo()
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::type_info* Animal::type_of_kind(Kind kind)
|
||||
{
|
||||
const std::type_info *Animal::type_of_kind(Kind kind) {
|
||||
switch (kind) {
|
||||
case Kind::Unknown:
|
||||
case Kind::Dog: break;
|
||||
case Kind::Dog:
|
||||
break;
|
||||
|
||||
case Kind::Labrador: return &typeid(Labrador);
|
||||
case Kind::Chihuahua: return &typeid(Chihuahua);
|
||||
case Kind::Labrador:
|
||||
return &typeid(Labrador);
|
||||
case Kind::Chihuahua:
|
||||
return &typeid(Chihuahua);
|
||||
|
||||
case Kind::LastDog:
|
||||
case Kind::Cat: break;
|
||||
case Kind::Panther: return &typeid(Panther);
|
||||
case Kind::LastCat: break;
|
||||
case Kind::Cat:
|
||||
break;
|
||||
case Kind::Panther:
|
||||
return &typeid(Panther);
|
||||
case Kind::LastCat:
|
||||
break;
|
||||
}
|
||||
|
||||
if (kind >= Kind::Dog && kind <= Kind::LastDog) {
|
||||
@ -109,25 +111,24 @@ const std::type_info* Animal::type_of_kind(Kind kind)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string Animal::name_of_kind(Kind kind)
|
||||
{
|
||||
std::string Animal::name_of_kind(Kind kind) {
|
||||
std::string raw_name = type_of_kind(kind)->name();
|
||||
py::detail::clean_type_id(raw_name);
|
||||
return raw_name;
|
||||
}
|
||||
|
||||
namespace pybind11 {
|
||||
template <typename itype>
|
||||
struct polymorphic_type_hook<itype, detail::enable_if_t<std::is_base_of<Animal, itype>::value>>
|
||||
{
|
||||
static const void *get(const itype *src, const std::type_info*& type)
|
||||
{ type = src ? Animal::type_of_kind(src->kind) : nullptr; return src; }
|
||||
};
|
||||
template <typename itype>
|
||||
struct polymorphic_type_hook<itype, detail::enable_if_t<std::is_base_of<Animal, itype>::value>> {
|
||||
static const void *get(const itype *src, const std::type_info *&type) {
|
||||
type = src ? Animal::type_of_kind(src->kind) : nullptr;
|
||||
return src;
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
||||
|
||||
TEST_SUBMODULE(tagbased_polymorphic, m) {
|
||||
py::class_<Animal>(m, "Animal")
|
||||
.def_readonly("name", &Animal::name);
|
||||
py::class_<Animal>(m, "Animal").def_readonly("name", &Animal::name);
|
||||
py::class_<Dog, Animal>(m, "Dog")
|
||||
.def(py::init<std::string>())
|
||||
.def_readwrite("sound", &Dog::sound)
|
||||
@ -138,9 +139,7 @@ TEST_SUBMODULE(tagbased_polymorphic, m) {
|
||||
py::class_<Chihuahua, Dog>(m, "Chihuahua")
|
||||
.def(py::init<std::string>())
|
||||
.def("bark", &Chihuahua::bark);
|
||||
py::class_<Cat, Animal>(m, "Cat")
|
||||
.def(py::init<std::string>())
|
||||
.def("purr", &Cat::purr);
|
||||
py::class_<Cat, Animal>(m, "Cat").def(py::init<std::string>()).def("purr", &Cat::purr);
|
||||
py::class_<Panther, Cat>(m, "Panther")
|
||||
.def(py::init<std::string>())
|
||||
.def("purr", &Panther::purr);
|
||||
|
||||
@ -10,20 +10,20 @@
|
||||
#include <pybind11/cast.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace {
|
||||
|
||||
struct IntStruct {
|
||||
explicit IntStruct(int v) : value(v) {};
|
||||
explicit IntStruct(int v) : value(v){};
|
||||
~IntStruct() { value = -value; }
|
||||
IntStruct(const IntStruct&) = default;
|
||||
IntStruct& operator=(const IntStruct&) = default;
|
||||
IntStruct(const IntStruct &) = default;
|
||||
IntStruct &operator=(const IntStruct &) = default;
|
||||
|
||||
int value;
|
||||
};
|
||||
|
||||
@ -7,13 +7,15 @@
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
#include "constructor_stats.h"
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "constructor_stats.h"
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
/* This is an example class that we'll want to be able to extend from Python */
|
||||
class ExampleVirt {
|
||||
class ExampleVirt {
|
||||
public:
|
||||
explicit ExampleVirt(int state) : state(state) { print_created(this, state); }
|
||||
ExampleVirt(const ExampleVirt &e) : state(e.state) { print_copy_created(this); }
|
||||
@ -25,7 +27,8 @@ public:
|
||||
|
||||
virtual int run(int value) {
|
||||
py::print("Original implementation of "
|
||||
"ExampleVirt::run(state={}, value={}, str1={}, str2={})"_s.format(state, value, get_string1(), *get_string2()));
|
||||
"ExampleVirt::run(state={}, value={}, str1={}, str2={})"_s.format(
|
||||
state, value, get_string1(), *get_string2()));
|
||||
return state + value;
|
||||
}
|
||||
|
||||
@ -33,8 +36,8 @@ public:
|
||||
virtual void pure_virtual() = 0;
|
||||
|
||||
// Returning a reference/pointer to a type converted from python (numbers, strings, etc.) is a
|
||||
// bit trickier, because the actual int& or std::string& or whatever only exists temporarily, so
|
||||
// we have to handle it specially in the trampoline class (see below).
|
||||
// bit trickier, because the actual int& or std::string& or whatever only exists temporarily,
|
||||
// so we have to handle it specially in the trampoline class (see below).
|
||||
virtual const std::string &get_string1() { return str1; }
|
||||
virtual const std::string *get_string2() { return &str2; }
|
||||
|
||||
@ -50,59 +53,53 @@ public:
|
||||
|
||||
int run(int value) override {
|
||||
/* Generate wrapping code that enables native function overloading */
|
||||
PYBIND11_OVERRIDE(
|
||||
int, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
run, /* Name of function */
|
||||
value /* Argument(s) */
|
||||
PYBIND11_OVERRIDE(int, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
run, /* Name of function */
|
||||
value /* Argument(s) */
|
||||
);
|
||||
}
|
||||
|
||||
bool run_bool() override {
|
||||
PYBIND11_OVERRIDE_PURE(
|
||||
bool, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
run_bool, /* Name of function */
|
||||
/* This function has no arguments. The trailing comma
|
||||
in the previous line is needed for some compilers */
|
||||
PYBIND11_OVERRIDE_PURE(bool, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
run_bool, /* Name of function */
|
||||
/* This function has no arguments. The trailing comma
|
||||
in the previous line is needed for some compilers */
|
||||
);
|
||||
}
|
||||
|
||||
void pure_virtual() override {
|
||||
PYBIND11_OVERRIDE_PURE(
|
||||
void, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
pure_virtual, /* Name of function */
|
||||
/* This function has no arguments. The trailing comma
|
||||
in the previous line is needed for some compilers */
|
||||
PYBIND11_OVERRIDE_PURE(void, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
pure_virtual, /* Name of function */
|
||||
/* This function has no arguments. The trailing comma
|
||||
in the previous line is needed for some compilers */
|
||||
);
|
||||
}
|
||||
|
||||
// We can return reference types for compatibility with C++ virtual interfaces that do so, but
|
||||
// note they have some significant limitations (see the documentation).
|
||||
const std::string &get_string1() override {
|
||||
PYBIND11_OVERRIDE(
|
||||
const std::string &, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
get_string1, /* Name of function */
|
||||
/* (no arguments) */
|
||||
PYBIND11_OVERRIDE(const std::string &, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
get_string1, /* Name of function */
|
||||
/* (no arguments) */
|
||||
);
|
||||
}
|
||||
|
||||
const std::string *get_string2() override {
|
||||
PYBIND11_OVERRIDE(
|
||||
const std::string *, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
get_string2, /* Name of function */
|
||||
/* (no arguments) */
|
||||
PYBIND11_OVERRIDE(const std::string *, /* Return type */
|
||||
ExampleVirt, /* Parent class */
|
||||
get_string2, /* Name of function */
|
||||
/* (no arguments) */
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class NonCopyable {
|
||||
public:
|
||||
NonCopyable(int a, int b) : value{new int(a*b)} { print_created(this, a, b); }
|
||||
NonCopyable(int a, int b) : value{new int(a * b)} { print_created(this, a, b); }
|
||||
NonCopyable(NonCopyable &&o) noexcept : value{std::move(o.value)} { print_move_created(this); }
|
||||
NonCopyable(const NonCopyable &) = delete;
|
||||
NonCopyable() = delete;
|
||||
@ -124,11 +121,12 @@ private:
|
||||
// when it is not referenced elsewhere, but copied if it is still referenced.
|
||||
class Movable {
|
||||
public:
|
||||
Movable(int a, int b) : value{a+b} { print_created(this, a, b); }
|
||||
Movable(int a, int b) : value{a + b} { print_created(this, a, b); }
|
||||
Movable(const Movable &m) : value{m.value} { print_copy_created(this); }
|
||||
Movable(Movable &&m) noexcept : value{m.value} { print_move_created(this); }
|
||||
std::string get_value() const { return std::to_string(value); }
|
||||
~Movable() { print_destroyed(this); }
|
||||
|
||||
private:
|
||||
int value;
|
||||
};
|
||||
@ -137,7 +135,7 @@ class NCVirt {
|
||||
public:
|
||||
virtual ~NCVirt() = default;
|
||||
NCVirt() = default;
|
||||
NCVirt(const NCVirt&) = delete;
|
||||
NCVirt(const NCVirt &) = delete;
|
||||
virtual NonCopyable get_noncopyable(int a, int b) { return NonCopyable(a, b); }
|
||||
virtual Movable get_movable(int a, int b) = 0;
|
||||
|
||||
@ -160,7 +158,7 @@ struct Base {
|
||||
virtual std::string dispatch() const { return {}; };
|
||||
virtual ~Base() = default;
|
||||
Base() = default;
|
||||
Base(const Base&) = delete;
|
||||
Base(const Base &) = delete;
|
||||
};
|
||||
|
||||
struct DispatchIssue : Base {
|
||||
@ -173,33 +171,33 @@ struct DispatchIssue : Base {
|
||||
// objects and send the result to the visitor functor
|
||||
struct AdderBase {
|
||||
struct Data {};
|
||||
using DataVisitor = std::function<void (const Data&)>;
|
||||
using DataVisitor = std::function<void(const Data &)>;
|
||||
|
||||
virtual void operator()(const Data& first, const Data& second, const DataVisitor& visitor) const = 0;
|
||||
virtual void
|
||||
operator()(const Data &first, const Data &second, const DataVisitor &visitor) const = 0;
|
||||
virtual ~AdderBase() = default;
|
||||
AdderBase() = default;
|
||||
AdderBase(const AdderBase&) = delete;
|
||||
AdderBase(const AdderBase &) = delete;
|
||||
};
|
||||
|
||||
struct Adder : AdderBase {
|
||||
void operator()(const Data& first, const Data& second, const DataVisitor& visitor) const override {
|
||||
PYBIND11_OVERRIDE_PURE_NAME(void, AdderBase, "__call__", operator(), first, second, visitor);
|
||||
void
|
||||
operator()(const Data &first, const Data &second, const DataVisitor &visitor) const override {
|
||||
PYBIND11_OVERRIDE_PURE_NAME(
|
||||
void, AdderBase, "__call__", operator(), first, second, visitor);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void test_gil() {
|
||||
{
|
||||
py::gil_scoped_acquire lock;
|
||||
py::print("1st lock acquired");
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
py::gil_scoped_acquire lock;
|
||||
py::print("2nd lock acquired");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void test_gil_from_thread() {
|
||||
@ -225,12 +223,12 @@ class test_override_cache_helper_trampoline : public test_override_cache_helper
|
||||
int func() override { PYBIND11_OVERRIDE(int, test_override_cache_helper, func); }
|
||||
};
|
||||
|
||||
inline int test_override_cache(std::shared_ptr<test_override_cache_helper> const &instance) { return instance->func(); }
|
||||
inline int test_override_cache(std::shared_ptr<test_override_cache_helper> const &instance) {
|
||||
return instance->func();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Forward declaration (so that we can put the main tests here; the inherited virtual approaches are
|
||||
// rather long).
|
||||
// Forward declaration (so that we can put the main tests here; the inherited virtual approaches
|
||||
// are rather long).
|
||||
void initialize_inherited_virtuals(py::module_ &m);
|
||||
|
||||
TEST_SUBMODULE(virtual_functions, m) {
|
||||
@ -242,11 +240,9 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
.def("run_bool", &ExampleVirt::run_bool)
|
||||
.def("pure_virtual", &ExampleVirt::pure_virtual);
|
||||
|
||||
py::class_<NonCopyable>(m, "NonCopyable")
|
||||
.def(py::init<int, int>());
|
||||
py::class_<NonCopyable>(m, "NonCopyable").def(py::init<int, int>());
|
||||
|
||||
py::class_<Movable>(m, "Movable")
|
||||
.def(py::init<int, int>());
|
||||
py::class_<Movable>(m, "Movable").def(py::init<int, int>());
|
||||
|
||||
// test_move_support
|
||||
#if !defined(__INTEL_COMPILER) && !defined(__CUDACC__) && !defined(__PGIC__)
|
||||
@ -259,7 +255,7 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
#endif
|
||||
|
||||
m.def("runExampleVirt", [](ExampleVirt *ex, int value) { return ex->run(value); });
|
||||
m.def("runExampleVirtBool", [](ExampleVirt* ex) { return ex->run_bool(); });
|
||||
m.def("runExampleVirtBool", [](ExampleVirt *ex) { return ex->run_bool(); });
|
||||
m.def("runExampleVirtVirtual", [](ExampleVirt *ex) { ex->pure_virtual(); });
|
||||
|
||||
m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>);
|
||||
@ -270,27 +266,25 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
// that were not extended on the Python side
|
||||
struct A {
|
||||
A() = default;
|
||||
A(const A&) = delete;
|
||||
A(const A &) = delete;
|
||||
virtual ~A() = default;
|
||||
virtual void f() { py::print("A.f()"); }
|
||||
};
|
||||
|
||||
struct PyA : A {
|
||||
PyA() { py::print("PyA.PyA()"); }
|
||||
PyA(const PyA&) = delete;
|
||||
PyA(const PyA &) = delete;
|
||||
~PyA() override { py::print("PyA.~PyA()"); }
|
||||
|
||||
void f() override {
|
||||
py::print("PyA.f()");
|
||||
// This convolution just gives a `void`, but tests that PYBIND11_TYPE() works to protect
|
||||
// a type containing a ,
|
||||
// This convolution just gives a `void`, but tests that PYBIND11_TYPE() works to
|
||||
// protect a type containing a ,
|
||||
PYBIND11_OVERRIDE(PYBIND11_TYPE(typename std::enable_if<true, void>::type), A, f);
|
||||
}
|
||||
};
|
||||
|
||||
py::class_<A, PyA>(m, "A")
|
||||
.def(py::init<>())
|
||||
.def("f", &A::f);
|
||||
py::class_<A, PyA>(m, "A").def(py::init<>()).def("f", &A::f);
|
||||
|
||||
m.def("call_f", [](A *a) { a->f(); });
|
||||
|
||||
@ -298,14 +292,14 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
// ... unless we explicitly request it, as in this example:
|
||||
struct A2 {
|
||||
A2() = default;
|
||||
A2(const A2&) = delete;
|
||||
A2(const A2 &) = delete;
|
||||
virtual ~A2() = default;
|
||||
virtual void f() { py::print("A2.f()"); }
|
||||
};
|
||||
|
||||
struct PyA2 : A2 {
|
||||
PyA2() { py::print("PyA2.PyA2()"); }
|
||||
PyA2(const PyA2&) = delete;
|
||||
PyA2(const PyA2 &) = delete;
|
||||
~PyA2() override { py::print("PyA2.~PyA2()"); }
|
||||
void f() override {
|
||||
py::print("PyA2.f()");
|
||||
@ -326,7 +320,7 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
.def(py::init<>())
|
||||
.def("dispatch", &Base::dispatch);
|
||||
|
||||
m.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
|
||||
m.def("dispatch_issue_go", [](const Base *b) { return b->dispatch(); });
|
||||
|
||||
// test_recursive_dispatch_issue
|
||||
// #3357: Recursive dispatch fails to find python function override
|
||||
@ -334,31 +328,39 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
.def(pybind11::init<>())
|
||||
.def("__call__", &AdderBase::operator());
|
||||
|
||||
pybind11::class_<AdderBase::Data>(m, "Data")
|
||||
.def(pybind11::init<>());
|
||||
pybind11::class_<AdderBase::Data>(m, "Data").def(pybind11::init<>());
|
||||
|
||||
m.def("add2", [](const AdderBase::Data& first, const AdderBase::Data& second,
|
||||
const AdderBase& adder, const AdderBase::DataVisitor& visitor) {
|
||||
adder(first, second, visitor);
|
||||
});
|
||||
m.def("add2",
|
||||
[](const AdderBase::Data &first,
|
||||
const AdderBase::Data &second,
|
||||
const AdderBase &adder,
|
||||
const AdderBase::DataVisitor &visitor) { adder(first, second, visitor); });
|
||||
|
||||
m.def("add3", [](const AdderBase::Data& first, const AdderBase::Data& second, const AdderBase::Data& third,
|
||||
const AdderBase& adder, const AdderBase::DataVisitor& visitor) {
|
||||
adder(first, second, [&] (const AdderBase::Data& first_plus_second) {
|
||||
adder(first_plus_second, third, visitor); // NOLINT(readability-suspicious-call-argument)
|
||||
});
|
||||
});
|
||||
m.def("add3",
|
||||
[](const AdderBase::Data &first,
|
||||
const AdderBase::Data &second,
|
||||
const AdderBase::Data &third,
|
||||
const AdderBase &adder,
|
||||
const AdderBase::DataVisitor &visitor) {
|
||||
adder(first, second, [&](const AdderBase::Data &first_plus_second) {
|
||||
adder(first_plus_second,
|
||||
third,
|
||||
visitor); // NOLINT(readability-suspicious-call-argument)
|
||||
});
|
||||
});
|
||||
|
||||
// test_override_ref
|
||||
// #392/397: overriding reference-returning functions
|
||||
class OverrideTest {
|
||||
public:
|
||||
struct A { std::string value = "hi"; };
|
||||
struct A {
|
||||
std::string value = "hi";
|
||||
};
|
||||
std::string v;
|
||||
A a;
|
||||
explicit OverrideTest(const std::string &v) : v{v} {}
|
||||
OverrideTest() = default;
|
||||
OverrideTest(const OverrideTest&) = delete;
|
||||
OverrideTest(const OverrideTest &) = delete;
|
||||
virtual std::string str_value() { return v; }
|
||||
virtual std::string &str_ref() { return v; }
|
||||
virtual A A_value() { return a; }
|
||||
@ -369,17 +371,22 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
class PyOverrideTest : public OverrideTest {
|
||||
public:
|
||||
using OverrideTest::OverrideTest;
|
||||
std::string str_value() override { PYBIND11_OVERRIDE(std::string, OverrideTest, str_value); }
|
||||
std::string str_value() override {
|
||||
PYBIND11_OVERRIDE(std::string, OverrideTest, str_value);
|
||||
}
|
||||
// Not allowed (enabling the below should hit a static_assert failure): we can't get a
|
||||
// reference to a python numeric value, since we only copy values in the numeric type
|
||||
// caster:
|
||||
#ifdef PYBIND11_NEVER_DEFINED_EVER
|
||||
std::string &str_ref() override { PYBIND11_OVERRIDE(std::string &, OverrideTest, str_ref); }
|
||||
std::string &str_ref() override {
|
||||
PYBIND11_OVERRIDE(std::string &, OverrideTest, str_ref);
|
||||
}
|
||||
#endif
|
||||
// But we can work around it like this:
|
||||
private:
|
||||
std::string _tmp;
|
||||
std::string str_ref_helper() { PYBIND11_OVERRIDE(std::string, OverrideTest, str_ref); }
|
||||
|
||||
public:
|
||||
std::string &str_ref() override { return _tmp = str_ref_helper(); }
|
||||
|
||||
@ -398,14 +405,15 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
.def("A_value", &OverrideTest::A_value)
|
||||
.def("A_ref", &OverrideTest::A_ref);
|
||||
|
||||
py::class_<test_override_cache_helper, test_override_cache_helper_trampoline, std::shared_ptr<test_override_cache_helper>>(m, "test_override_cache_helper")
|
||||
py::class_<test_override_cache_helper,
|
||||
test_override_cache_helper_trampoline,
|
||||
std::shared_ptr<test_override_cache_helper>>(m, "test_override_cache_helper")
|
||||
.def(py::init_alias<>())
|
||||
.def("func", &test_override_cache_helper::func);
|
||||
|
||||
m.def("test_override_cache", test_override_cache);
|
||||
}
|
||||
|
||||
|
||||
// Inheriting virtual methods. We do two versions here: the repeat-everything version and the
|
||||
// templated trampoline versions mentioned in docs/advanced.rst.
|
||||
//
|
||||
@ -414,83 +422,96 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
// properly (pybind11, sensibly, doesn't allow us to bind the same C++ class to
|
||||
// multiple python classes).
|
||||
class A_Repeat {
|
||||
#define A_METHODS \
|
||||
public: \
|
||||
virtual int unlucky_number() = 0; \
|
||||
virtual std::string say_something(unsigned times) { \
|
||||
std::string s = ""; \
|
||||
for (unsigned i = 0; i < times; ++i) \
|
||||
s += "hi"; \
|
||||
return s; \
|
||||
} \
|
||||
std::string say_everything() { \
|
||||
return say_something(1) + " " + std::to_string(unlucky_number()); \
|
||||
#define A_METHODS \
|
||||
public: \
|
||||
virtual int unlucky_number() = 0; \
|
||||
virtual std::string say_something(unsigned times) { \
|
||||
std::string s = ""; \
|
||||
for (unsigned i = 0; i < times; ++i) \
|
||||
s += "hi"; \
|
||||
return s; \
|
||||
} \
|
||||
std::string say_everything() { \
|
||||
return say_something(1) + " " + std::to_string(unlucky_number()); \
|
||||
}
|
||||
A_METHODS
|
||||
A_METHODS
|
||||
A_Repeat() = default;
|
||||
A_Repeat(const A_Repeat&) = delete;
|
||||
A_Repeat(const A_Repeat &) = delete;
|
||||
virtual ~A_Repeat() = default;
|
||||
};
|
||||
class B_Repeat : public A_Repeat {
|
||||
#define B_METHODS \
|
||||
public: \
|
||||
int unlucky_number() override { return 13; } \
|
||||
std::string say_something(unsigned times) override { \
|
||||
return "B says hi " + std::to_string(times) + " times"; \
|
||||
} \
|
||||
#define B_METHODS \
|
||||
public: \
|
||||
int unlucky_number() override { return 13; } \
|
||||
std::string say_something(unsigned times) override { \
|
||||
return "B says hi " + std::to_string(times) + " times"; \
|
||||
} \
|
||||
virtual double lucky_number() { return 7.0; }
|
||||
B_METHODS
|
||||
B_METHODS
|
||||
};
|
||||
class C_Repeat : public B_Repeat {
|
||||
#define C_METHODS \
|
||||
public: \
|
||||
int unlucky_number() override { return 4444; } \
|
||||
#define C_METHODS \
|
||||
public: \
|
||||
int unlucky_number() override { return 4444; } \
|
||||
double lucky_number() override { return 888; }
|
||||
C_METHODS
|
||||
C_METHODS
|
||||
};
|
||||
class D_Repeat : public C_Repeat {
|
||||
#define D_METHODS // Nothing overridden.
|
||||
D_METHODS
|
||||
D_METHODS
|
||||
};
|
||||
|
||||
// Base classes for templated inheritance trampolines. Identical to the repeat-everything version:
|
||||
class A_Tpl {
|
||||
A_METHODS;
|
||||
A_Tpl() = default;
|
||||
A_Tpl(const A_Tpl&) = delete;
|
||||
A_Tpl(const A_Tpl &) = delete;
|
||||
virtual ~A_Tpl() = default;
|
||||
};
|
||||
class B_Tpl : public A_Tpl { B_METHODS };
|
||||
class C_Tpl : public B_Tpl { C_METHODS };
|
||||
class D_Tpl : public C_Tpl { D_METHODS };
|
||||
|
||||
class B_Tpl : public A_Tpl {
|
||||
B_METHODS
|
||||
};
|
||||
class C_Tpl : public B_Tpl {
|
||||
C_METHODS
|
||||
};
|
||||
class D_Tpl : public C_Tpl {
|
||||
D_METHODS
|
||||
};
|
||||
|
||||
// Inheritance approach 1: each trampoline gets every virtual method (11 in total)
|
||||
class PyA_Repeat : public A_Repeat {
|
||||
public:
|
||||
using A_Repeat::A_Repeat;
|
||||
int unlucky_number() override { PYBIND11_OVERRIDE_PURE(int, A_Repeat, unlucky_number, ); }
|
||||
std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, A_Repeat, say_something, times); }
|
||||
std::string say_something(unsigned times) override {
|
||||
PYBIND11_OVERRIDE(std::string, A_Repeat, say_something, times);
|
||||
}
|
||||
};
|
||||
class PyB_Repeat : public B_Repeat {
|
||||
public:
|
||||
using B_Repeat::B_Repeat;
|
||||
int unlucky_number() override { PYBIND11_OVERRIDE(int, B_Repeat, unlucky_number, ); }
|
||||
std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, B_Repeat, say_something, times); }
|
||||
std::string say_something(unsigned times) override {
|
||||
PYBIND11_OVERRIDE(std::string, B_Repeat, say_something, times);
|
||||
}
|
||||
double lucky_number() override { PYBIND11_OVERRIDE(double, B_Repeat, lucky_number, ); }
|
||||
};
|
||||
class PyC_Repeat : public C_Repeat {
|
||||
public:
|
||||
using C_Repeat::C_Repeat;
|
||||
int unlucky_number() override { PYBIND11_OVERRIDE(int, C_Repeat, unlucky_number, ); }
|
||||
std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, C_Repeat, say_something, times); }
|
||||
std::string say_something(unsigned times) override {
|
||||
PYBIND11_OVERRIDE(std::string, C_Repeat, say_something, times);
|
||||
}
|
||||
double lucky_number() override { PYBIND11_OVERRIDE(double, C_Repeat, lucky_number, ); }
|
||||
};
|
||||
class PyD_Repeat : public D_Repeat {
|
||||
public:
|
||||
using D_Repeat::D_Repeat;
|
||||
int unlucky_number() override { PYBIND11_OVERRIDE(int, D_Repeat, unlucky_number, ); }
|
||||
std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, D_Repeat, say_something, times); }
|
||||
std::string say_something(unsigned times) override {
|
||||
PYBIND11_OVERRIDE(std::string, D_Repeat, say_something, times);
|
||||
}
|
||||
double lucky_number() override { PYBIND11_OVERRIDE(double, D_Repeat, lucky_number, ); }
|
||||
};
|
||||
|
||||
@ -513,7 +534,9 @@ class PyA_Tpl : public Base {
|
||||
public:
|
||||
using Base::Base; // Inherit constructors
|
||||
int unlucky_number() override { PYBIND11_OVERRIDE_PURE(int, Base, unlucky_number, ); }
|
||||
std::string say_something(unsigned times) override { PYBIND11_OVERRIDE(std::string, Base, say_something, times); }
|
||||
std::string say_something(unsigned times) override {
|
||||
PYBIND11_OVERRIDE(std::string, Base, say_something, times);
|
||||
}
|
||||
};
|
||||
template <class Base = B_Tpl>
|
||||
class PyB_Tpl : public PyA_Tpl<Base> {
|
||||
@ -548,10 +571,8 @@ void initialize_inherited_virtuals(py::module_ &m) {
|
||||
py::class_<B_Repeat, A_Repeat, PyB_Repeat>(m, "B_Repeat")
|
||||
.def(py::init<>())
|
||||
.def("lucky_number", &B_Repeat::lucky_number);
|
||||
py::class_<C_Repeat, B_Repeat, PyC_Repeat>(m, "C_Repeat")
|
||||
.def(py::init<>());
|
||||
py::class_<D_Repeat, C_Repeat, PyD_Repeat>(m, "D_Repeat")
|
||||
.def(py::init<>());
|
||||
py::class_<C_Repeat, B_Repeat, PyC_Repeat>(m, "C_Repeat").def(py::init<>());
|
||||
py::class_<D_Repeat, C_Repeat, PyD_Repeat>(m, "D_Repeat").def(py::init<>());
|
||||
|
||||
// test_
|
||||
// Method 2: Templated trampolines
|
||||
@ -563,11 +584,8 @@ void initialize_inherited_virtuals(py::module_ &m) {
|
||||
py::class_<B_Tpl, A_Tpl, PyB_Tpl<>>(m, "B_Tpl")
|
||||
.def(py::init<>())
|
||||
.def("lucky_number", &B_Tpl::lucky_number);
|
||||
py::class_<C_Tpl, B_Tpl, PyB_Tpl<C_Tpl>>(m, "C_Tpl")
|
||||
.def(py::init<>());
|
||||
py::class_<D_Tpl, C_Tpl, PyB_Tpl<D_Tpl>>(m, "D_Tpl")
|
||||
.def(py::init<>());
|
||||
|
||||
py::class_<C_Tpl, B_Tpl, PyB_Tpl<C_Tpl>>(m, "C_Tpl").def(py::init<>());
|
||||
py::class_<D_Tpl, C_Tpl, PyB_Tpl<D_Tpl>>(m, "D_Tpl").def(py::init<>());
|
||||
|
||||
// Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
|
||||
m.def("test_gil", &test_gil);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user