Browse Source

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>
pull/3718/head
Ralf W. Grosse-Kunstleve 3 years ago committed by GitHub
parent
commit
ec24786eab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      .pre-commit-config.yaml
  2. 207
      include/pybind11/attr.h
  3. 104
      include/pybind11/buffer_info.h
  4. 743
      include/pybind11/cast.h
  5. 115
      include/pybind11/chrono.h
  6. 23
      include/pybind11/complex.h
  7. 66
      include/pybind11/detail/class.h
  8. 882
      include/pybind11/detail/common.h
  9. 81
      include/pybind11/detail/descr.h
  10. 238
      include/pybind11/detail/init.h
  11. 115
      include/pybind11/detail/internals.h
  12. 309
      include/pybind11/detail/type_caster_base.h
  13. 9
      include/pybind11/detail/typeid.h
  14. 395
      include/pybind11/eigen.h
  15. 34
      include/pybind11/embed.h
  16. 87
      include/pybind11/eval.h
  17. 13
      include/pybind11/functional.h
  18. 30
      include/pybind11/gil.h
  19. 41
      include/pybind11/iostream.h
  20. 1027
      include/pybind11/numpy.h
  21. 253
      include/pybind11/operators.h
  22. 42
      include/pybind11/options.h
  23. 1211
      include/pybind11/pybind11.h
  24. 816
      include/pybind11/pytypes.h
  25. 139
      include/pybind11/stl.h
  26. 46
      include/pybind11/stl/filesystem.h
  27. 465
      include/pybind11/stl_bind.h
  28. 124
      tests/constructor_stats.h
  29. 37
      tests/cross_module_gil_utils.cpp
  30. 41
      tests/local_bindings.h
  31. 61
      tests/object.h
  32. 86
      tests/pybind11_cross_module_tests.cpp
  33. 20
      tests/pybind11_tests.cpp
  34. 29
      tests/pybind11_tests.h
  35. 5
      tests/test_async.cpp
  36. 67
      tests/test_buffers.cpp
  37. 298
      tests/test_builtin_casters.cpp
  38. 43
      tests/test_call_policies.cpp
  39. 60
      tests/test_callbacks.cpp
  40. 33
      tests/test_chrono.cpp
  41. 265
      tests/test_class.cpp
  42. 67
      tests/test_constants_and_functions.cpp
  43. 163
      tests/test_copy_move.cpp
  44. 100
      tests/test_custom_type_casters.cpp
  45. 55
      tests/test_docstring_options.cpp
  46. 185
      tests/test_eigen.cpp
  47. 8
      tests/test_embed/catch.cpp
  48. 9
      tests/test_embed/external_module.cpp
  49. 59
      tests/test_embed/test_interpreter.cpp
  50. 83
      tests/test_enum.cpp
  51. 28
      tests/test_eval.cpp
  52. 95
      tests/test_exceptions.cpp
  53. 1
      tests/test_exceptions.h
  54. 83
      tests/test_factory_constructors.cpp
  55. 44
      tests/test_gil_scoped.cpp
  56. 30
      tests/test_iostream.cpp
  57. 241
      tests/test_kwargs_and_defaults.cpp
  58. 38
      tests/test_local_bindings.cpp
  59. 194
      tests/test_methods_and_attributes.cpp
  60. 66
      tests/test_modules.cpp
  61. 137
      tests/test_multiple_inheritance.cpp
  62. 249
      tests/test_numpy_array.cpp
  63. 240
      tests/test_numpy_dtypes.cpp
  64. 32
      tests/test_numpy_vectorize.cpp
  65. 15
      tests/test_opaque_types.cpp
  66. 176
      tests/test_operator_overloading.cpp
  67. 6
      tests/test_pickling.cpp
  68. 167
      tests/test_pytypes.cpp
  69. 225
      tests/test_sequences_and_iterators.cpp
  70. 90
      tests/test_smart_ptr.cpp
  71. 236
      tests/test_stl.cpp
  72. 54
      tests/test_stl_binders.cpp
  73. 85
      tests/test_tagbased_polymorphic.cpp
  74. 10
      tests/test_thread.cpp
  75. 278
      tests/test_virtual_functions.cpp

10
.pre-commit-config.yaml

@ -141,11 +141,7 @@ repos: @@ -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

207
include/pybind11/attr.h

@ -20,54 +20,62 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -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 { @@ -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 { @@ -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 { }; @@ -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) @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { }; @@ -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) { @@ -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> { @@ -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> { @@ -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> { @@ -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 @@ -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>> { @@ -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 <> @@ -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 @@ -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;

104
include/pybind11/buffer_info.h

@ -44,8 +44,8 @@ struct buffer_info { @@ -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 { @@ -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 { @@ -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(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(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) {}
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 { @@ -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 { @@ -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: @@ -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")));
}
};

743
include/pybind11/cast.h

File diff suppressed because it is too large Load Diff

115
include/pybind11/chrono.h

@ -15,25 +15,25 @@ @@ -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: @@ -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: @@ -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: @@ -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) { @@ -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: @@ -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: @@ -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: @@ -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: @@ -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)

23
include/pybind11/complex.h

@ -10,36 +10,42 @@ @@ -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: @@ -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());
}

66
include/pybind11/detail/class.h

@ -16,12 +16,13 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -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() { @@ -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() { @@ -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() { @@ -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 @@ -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) { @@ -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) { @@ -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) { @@ -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() { @@ -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 @@ -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 *) { @@ -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) { @@ -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) { @@ -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) { @@ -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();

882
include/pybind11/detail/common.h

File diff suppressed because it is too large Load Diff

81
include/pybind11/detail/descr.h

@ -15,9 +15,9 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -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 { @@ -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 { @@ -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); }
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 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);
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)

238
include/pybind11/detail/init.h

@ -22,7 +22,8 @@ public: @@ -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) { @@ -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) { @@ -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 @@ -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> @@ -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) { @@ -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) { @@ -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> @@ -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) { @@ -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) { @@ -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 { @@ -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...)> { @@ -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...)> { @@ -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) { @@ -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) @@ -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)> { @@ -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...);
}
};

115
include/pybind11/detail/internals.h

@ -10,6 +10,7 @@ @@ -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> @@ -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 { @@ -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 { @@ -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 { @@ -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) { @@ -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() { @@ -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 { @@ -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) { @@ -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);

309
include/pybind11/detail/type_caster_base.h

@ -14,6 +14,7 @@ @@ -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) @@ -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: @@ -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_ @@ -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) @@ -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) { @@ -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, @@ -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 @@ -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 { @@ -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 { @@ -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 { @@ -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: @@ -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: @@ -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 @@ -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() { @@ -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() { @@ -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() { @@ -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() { @@ -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() { @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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 = @@ -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) @@ -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> {}; @@ -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: @@ -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: @@ -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))));
};

9
include/pybind11/detail/typeid.h

@ -13,7 +13,7 @@ @@ -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) { @@ -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) { @@ -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;

395
include/pybind11/eigen.h

@ -23,122 +23,145 @@ @@ -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
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;
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;
// 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 { @@ -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 { @@ -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 { @@ -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 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 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("]");
};
// 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()) { @@ -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) { @@ -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>> { @@ -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: @@ -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: @@ -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: @@ -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: @@ -469,8 +499,7 @@ public:
} else {
copy_or_ref = std::move(aref);
}
}
else {
} else {
need_copy = true;
}
}
@ -496,54 +525,76 @@ public: @@ -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: @@ -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: @@ -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>> { @@ -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>> { @@ -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)

34
include/pybind11/embed.h

@ -16,21 +16,17 @@ @@ -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) @@ -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) { @@ -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) { @@ -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;

87
include/pybind11/eval.h

@ -11,24 +11,24 @@ @@ -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()) @@ -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()) @@ -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()) @@ -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()) @@ -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();

13
include/pybind11/functional.h

@ -10,6 +10,7 @@ @@ -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> @@ -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: @@ -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: @@ -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)

30
include/pybind11/gil.h

@ -14,7 +14,6 @@ @@ -14,7 +14,6 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
// forward declarations
@ -22,7 +21,6 @@ PyThreadState *get_thread_state_unchecked(); @@ -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: @@ -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: @@ -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: @@ -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: @@ -126,6 +120,7 @@ public:
PyEval_SaveThread();
}
}
private:
PyThreadState *tstate = nullptr;
bool release = true;
@ -154,9 +149,7 @@ public: @@ -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: @@ -173,6 +166,7 @@ public:
PYBIND11_TLS_REPLACE_VALUE(key, tstate);
}
}
private:
PyThreadState *tstate;
bool disassoc;
@ -181,6 +175,7 @@ private: @@ -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: @@ -189,6 +184,7 @@ public:
class gil_scoped_release {
PyThreadState *state;
public:
gil_scoped_release() { state = PyEval_SaveThread(); }
~gil_scoped_release() { PyEval_RestoreThread(state); }

41
include/pybind11/iostream.h

@ -58,31 +58,23 @@ private: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -213,7 +197,6 @@ public:
: scoped_ostream_redirect(costream, pyostream) {}
};
PYBIND11_NAMESPACE_BEGIN(detail)
// Class to redirect output as a context manager. C++ backend.

1027
include/pybind11/numpy.h

File diff suppressed because it is too large Load Diff

253
include/pybind11/operators.h

@ -16,12 +16,50 @@ PYBIND11_NAMESPACE_BEGIN(detail) @@ -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 { @@ -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_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>(); \
}
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)
#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_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)
// 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

42
include/pybind11/options.h

@ -15,44 +15,54 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -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() {

1211
include/pybind11/pybind11.h

File diff suppressed because it is too large Load Diff

816
include/pybind11/pytypes.h

File diff suppressed because it is too large Load Diff

139
include/pybind11/stl.h

@ -9,26 +9,27 @@ @@ -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) @@ -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) { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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: @@ -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: @@ -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: @@ -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: @@ -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 @@ -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 @@ -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 { @@ -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 { @@ -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...>> { @@ -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)

46
include/pybind11/stl/filesystem.h

@ -4,21 +4,20 @@ @@ -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) @@ -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: @@ -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: @@ -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: @@ -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)

465
include/pybind11/stl_bind.h

@ -19,74 +19,87 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -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(...);
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;
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_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_> @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) @@ -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) @@ -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) @@ -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) @@ -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 @@ -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 @@ -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() { @@ -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) { @@ -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) { @@ -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) @@ -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 @@ -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 @@ -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 @@ -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_ & @@ -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 &map;
};
template<typename Map>
struct values_view
{
template <typename Map>
struct values_view {
Map &map;
};
template<typename Map>
struct items_view
{
template <typename Map>
struct items_view {
Map &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&&. @@ -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 */
);

124
tests/constructor_stats.h

@ -56,7 +56,8 @@ from the ConstructorStats instance `.values()` method. @@ -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 @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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...);
}

37
tests/cross_module_gil_utils.cpp

@ -7,6 +7,7 @@ @@ -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; } @@ -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() @@ -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

41
tests/local_bindings.h

@ -1,12 +1,13 @@ @@ -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>; @@ -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: @@ -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); @@ -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: @@ -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} {}
};

61
tests/object.h

@ -1,8 +1,9 @@ @@ -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: @@ -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 {}; @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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: @@ -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;
};

86
tests/pybind11_cross_module_tests.cpp

@ -7,12 +7,12 @@ @@ -7,12 +7,12 @@
BSD-style license that can be found in the LICENSE file.
*/
#include "pybind11_tests.h"
#include <pybind11/stl_bind.h>
#include "local_bindings.h"
#include "pybind11_tests.h"
#include "test_exceptions.h"
#include <pybind11/stl_bind.h>
#include <numeric>
#include <utility>
@ -30,39 +30,45 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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>());

20
tests/pybind11_tests.cpp

@ -8,6 +8,7 @@ @@ -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() { @@ -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) { @@ -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) { @@ -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);

29
tests/pybind11_tests.h

@ -1,11 +1,12 @@ @@ -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: @@ -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 { @@ -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 { @@ -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) { @@ -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)));
}

5
tests/test_async.cpp

@ -11,12 +11,11 @@ @@ -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")();

67
tests/test_buffers.cpp

@ -7,10 +7,11 @@ @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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(); });
}

298
tests/test_builtin_casters.cpp

@ -7,64 +7,67 @@ @@ -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>();
// 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 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&&>>>>;
private:
ConstRefCasted value = {0};
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; }
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;
}
// 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};
};
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(pybind11)
@ -74,25 +77,43 @@ TEST_SUBMODULE(builtin_casters, m) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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; });
}

43
tests/test_call_policies.cpp

@ -40,18 +40,17 @@ TEST_SUBMODULE(call_policies, m) { @@ -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) { @@ -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) { @@ -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_wrong_order", []() {
return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status();
}, py::call_guard<DependentGuard, 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_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,

60
tests/test_callbacks.cpp

@ -7,11 +7,12 @@ @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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)>;

33
tests/test_chrono.cpp

@ -8,21 +8,20 @@ @@ -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) { @@ -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) { @@ -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);
}

265
tests/test_class.cpp

@ -11,18 +11,19 @@ @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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>>; @@ -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

67
tests/test_constants_and_functions.cpp

@ -12,20 +12,14 @@ @@ -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) { @@ -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) { @@ -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) { @@ -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(

163
tests/test_copy_move.cpp

@ -8,30 +8,33 @@ @@ -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: @@ -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: @@ -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: @@ -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) @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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);
}

100
tests/test_custom_type_casters.cpp

@ -7,16 +7,23 @@ @@ -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: @@ -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: @@ -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: @@ -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: @@ -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) { @@ -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) { @@ -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);
}

55
tests/test_docstring_options.cpp

@ -15,35 +15,52 @@ TEST_SUBMODULE(docstring_options, m) { @@ -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_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_overloaded3", [](int) {}, py::arg("i"));
m.def("test_overloaded3", [](double) {}, py::arg("d"), "Overload docstr");
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_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");
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) { @@ -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");
}
}

185
tests/test_eigen.cpp

@ -7,27 +7,27 @@ @@ -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); }; @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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

8
tests/test_embed/catch.cpp

@ -4,14 +4,14 @@ @@ -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

9
tests/test_embed/external_module.cpp

@ -13,11 +13,8 @@ PYBIND11_MODULE(external_module, m) { @@ -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()); });
}

59
tests/test_embed/test_interpreter.cpp

@ -1,13 +1,12 @@ @@ -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) { @@ -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") { @@ -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") { @@ -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") { @@ -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") { @@ -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") { @@ -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") { @@ -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") { @@ -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());

83
tests/test_enum.cpp

@ -11,11 +11,7 @@ @@ -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) { @@ -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) { @@ -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) { @@ -50,14 +39,9 @@ TEST_SUBMODULE(enums, m) {
// test_implicit_conversion
class ClassWithUnscopedEnum {
public:
enum EMode {
EFirstMode = 1,
ESecondMode
};
static EMode test_function(EMode mode) {
return mode;
}
enum EMode { EFirstMode = 1, ESecondMode };
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) { @@ -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) { @@ -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);

28
tests/test_eval.cpp

@ -7,10 +7,10 @@ @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -114,7 +110,9 @@ TEST_SUBMODULE(eval_, m) {
def func_local():
return local_value
)", global, local);
)",
global,
local);
return std::make_pair(global, local);
});
}

95
tests/test_exceptions.cpp

@ -9,8 +9,8 @@ @@ -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 @@ @@ -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: @@ -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: @@ -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: @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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
tests/test_exceptions.h

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
#pragma once
#include "pybind11_tests.h"
#include <stdexcept>
// shared exceptions for cross_module_tests

83
tests/test_factory_constructors.cpp

@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
#include "constructor_stats.h"
#include "pybind11_tests.h"
#include <cmath>
#include <new>
#include <utility>
@ -87,6 +88,7 @@ class TestFactory6 { @@ -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 { @@ -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: @@ -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: @@ -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: @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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 {};

44
tests/test_gil_scoped.cpp

@ -7,43 +7,41 @@ @@ -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);
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();
});
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();
});
}

30
tests/test_iostream.cpp

@ -7,12 +7,14 @@ @@ -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 { @@ -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 { @@ -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) { @@ -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) { @@ -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; });

241
tests/test_kwargs_and_defaults.cpp

@ -7,39 +7,43 @@ @@ -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) { @@ -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("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(); });
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);
// 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) { @@ -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) { @@ -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) { @@ -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"));
}

38
tests/test_local_bindings.cpp

@ -8,12 +8,12 @@ @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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>());

194
tests/test_methods_and_attributes.cpp

@ -8,8 +8,8 @@ @@ -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: @@ -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: @@ -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 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
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)"; }
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 &&) {}
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
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); @@ -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 { @@ -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 { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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

66
tests/test_modules.cpp

@ -8,8 +8,8 @@ @@ -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) { @@ -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 &copy) { print_copy_assigned(this); v = copy.v; return *this; }
A(const A &) { print_copy_created(this); }
A &operator=(const A &copy) {
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 &copy) { print_copy_assigned(this); a1 = copy.a1; a2 = copy.a2; return *this; }
B(const B &) { print_copy_created(this); }
B &operator=(const B &copy) {
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) { @@ -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) { @@ -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) { @@ -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;
});

137
tests/test_multiple_inheritance.cpp

@ -8,14 +8,15 @@ @@ -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 { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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}

249
tests/test_numpy_array.cpp

@ -7,11 +7,11 @@ @@ -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 { @@ -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) { @@ -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; @@ -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) { @@ -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) { @@ -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; }
#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); }); \
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); }); \
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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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");

240
tests/test_numpy_dtypes.cpp

@ -7,13 +7,14 @@ @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -70,7 +71,7 @@ struct PartialNestedStruct {
uint64_t dummy2;
};
struct UnboundStruct { };
struct UnboundStruct {};
struct StringStruct {
char a[3];
@ -82,7 +83,7 @@ struct ComplexStruct { @@ -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 { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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() { @@ -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 {}; @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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[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[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[3].a[2] = 'c';
ptr[3].b[2] = 'c';
}
return arr;
});
@ -513,14 +551,19 @@ TEST_SUBMODULE(numpy_dtypes, m) { @@ -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) { @@ -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)); });

32
tests/test_numpy_vectorize.cpp

@ -8,38 +8,43 @@ @@ -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) { @@ -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) { @@ -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) { @@ -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; }));
}

15
tests/test_opaque_types.cpp

@ -7,8 +7,10 @@ @@ -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) { @@ -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:

176
tests/test_operator_overloading.cpp

@ -7,11 +7,13 @@ @@ -7,11 +7,13 @@
BSD-style license that can be found in the LICENSE file.
*/
#include <pybind11/operators.h>
#include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
#include <functional>
#include <pybind11/operators.h>
#include <pybind11/stl.h>
class Vector2 {
public:
@ -21,17 +23,24 @@ public: @@ -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: @@ -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 @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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

6
tests/test_pickling.cpp

@ -20,11 +20,11 @@ @@ -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) { @@ -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);
}));

167
tests/test_pytypes.cpp

@ -7,22 +7,21 @@ @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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); });

225
tests/test_sequences_and_iterators.cpp

@ -8,44 +8,52 @@ @@ -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: @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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); });
}

90
tests/test_smart_ptr.cpp

@ -8,21 +8,23 @@ @@ -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: @@ -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: @@ -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: @@ -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 { @@ -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: @@ -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: @@ -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: @@ -117,6 +126,7 @@ public:
delete o;
}
}
private:
~MyObject4() {
myobject4_instances.erase(this);
@ -144,6 +154,7 @@ public: @@ -144,6 +154,7 @@ public:
delete o;
}
}
protected:
virtual ~MyObject4a() {
myobject4a_instances.erase(this);
@ -232,14 +243,14 @@ struct TypeForMoveOnlyHolderWithAddressOf { @@ -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 { @@ -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) { @@ -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) { @@ -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) { @@ -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")

236
tests/test_stl.cpp

@ -7,39 +7,43 @@ @@ -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> { @@ -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 { @@ -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: @@ -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: @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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>())

54
tests/test_stl_binders.cpp

@ -7,12 +7,13 @@ @@ -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: @@ -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: @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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());

85
tests/test_tagbased_polymorphic.cpp

@ -7,11 +7,11 @@ @@ -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 @@ -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() @@ -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) @@ -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) { @@ -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
tests/test_thread.cpp

@ -10,20 +10,20 @@ @@ -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;
};

278
tests/test_virtual_functions.cpp

@ -7,13 +7,15 @@ @@ -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: @@ -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: @@ -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: @@ -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: @@ -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 { @@ -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 { @@ -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 { @@ -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 @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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<>());
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)
});
});
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("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) { @@ -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) { @@ -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) { @@ -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 { @@ -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) { @@ -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) { @@ -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…
Cancel
Save