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:
entry: PyBind|Numpy|Cmake|CCache|PyTest entry: PyBind|Numpy|Cmake|CCache|PyTest
exclude: .pre-commit-config.yaml exclude: .pre-commit-config.yaml
- repo: local - repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v13.0.0"
hooks: hooks:
- id: check-style - id: clang-format
name: Classic check-style
language: system
types:
- c++
entry: ./tools/check-style.sh

207
include/pybind11/attr.h

@ -20,54 +20,62 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
/// @{ /// @{
/// Annotation for methods /// Annotation for methods
struct is_method { handle class_; struct is_method {
handle class_;
explicit is_method(const handle &c) : class_(c) {} explicit is_method(const handle &c) : class_(c) {}
}; };
/// Annotation for operators /// Annotation for operators
struct is_operator { }; struct is_operator {};
/// Annotation for classes that cannot be subclassed /// Annotation for classes that cannot be subclassed
struct is_final { }; struct is_final {};
/// Annotation for parent scope /// Annotation for parent scope
struct scope { handle value; struct scope {
handle value;
explicit scope(const handle &s) : value(s) {} explicit scope(const handle &s) : value(s) {}
}; };
/// Annotation for documentation /// Annotation for documentation
struct doc { const char *value; struct doc {
const char *value;
explicit doc(const char *value) : value(value) {} explicit doc(const char *value) : value(value) {}
}; };
/// Annotation for function names /// Annotation for function names
struct name { const char *value; struct name {
const char *value;
explicit name(const char *value) : value(value) {} explicit name(const char *value) : value(value) {}
}; };
/// Annotation indicating that a function is an overload associated with a given "sibling" /// 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()) {} explicit sibling(const handle &value) : value(value.ptr()) {}
}; };
/// Annotation indicating that a class derives from another given type /// 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_") PYBIND11_DEPRECATED(
base() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute "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 /// 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 /// 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 /// 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 /// 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 /// Annotation which requests that a special metaclass is created for a type
struct metaclass { struct metaclass {
@ -78,7 +86,7 @@ struct metaclass {
metaclass() {} metaclass() {}
/// Override pybind11's default 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 /// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that
@ -99,15 +107,16 @@ struct custom_type_setup {
}; };
/// Annotation that marks a class as local to the module: /// 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) {} constexpr explicit module_local(bool v = true) : value(v) {}
}; };
/// Annotation to mark enums as an arithmetic type /// 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 /// Mark a function for addition at the beginning of the existing overload chain instead of the end
struct prepend { }; struct prepend {};
/** \rst /** \rst
A call policy which places one or more guard variables (``Ts...``) around the function call. A call policy which places one or more guard variables (``Ts...``) around the function call.
@ -127,9 +136,13 @@ struct prepend { };
return foo(args...); // forwarded arguments return foo(args...); // forwarded arguments
}); });
\endrst */ \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> template <typename T>
struct call_guard<T> { struct call_guard<T> {
@ -154,7 +167,8 @@ PYBIND11_NAMESPACE_BEGIN(detail)
enum op_id : int; enum op_id : int;
enum op_type : int; enum op_type : int;
struct undefined_t; 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); 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 /// Internal data structure which holds metadata about a keyword argument
@ -166,15 +180,16 @@ struct argument_record {
bool none : 1; ///< True if None is allowed when loading bool none : 1; ///< True if None is allowed when loading
argument_record(const char *name, const char *descr, handle value, bool convert, bool none) 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 { struct function_record {
function_record() function_record()
: is_constructor(false), is_new_style_constructor(false), is_stateless(false), : is_constructor(false), is_new_style_constructor(false), is_stateless(false),
is_operator(false), is_method(false), has_args(false), is_operator(false), is_method(false), has_args(false), has_kwargs(false),
has_kwargs(false), prepend(false) { } prepend(false) {}
/// Function name /// Function name
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
@ -189,13 +204,13 @@ struct function_record {
std::vector<argument_record> args; std::vector<argument_record> args;
/// Pointer to lambda function which converts arguments and performs the actual call /// 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 /// 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) /// 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 associated with this function
return_value_policy policy = return_value_policy::automatic; return_value_policy policy = return_value_policy::automatic;
@ -251,7 +266,7 @@ struct function_record {
struct type_record { struct type_record {
PYBIND11_NOINLINE type_record() PYBIND11_NOINLINE type_record()
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), : 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 to the parent scope
handle scope; handle scope;
@ -310,22 +325,22 @@ struct type_record {
/// Is the class inheritable from python classes? /// Is the class inheritable from python classes?
bool is_final : 1; 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); auto *base_info = detail::get_type_info(base, false);
if (!base_info) { if (!base_info) {
std::string tname(base.name()); std::string tname(base.name());
detail::clean_type_id(tname); detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(name) + pybind11_fail("generic_type: type \"" + std::string(name)
"\" referenced unknown base type \"" + tname + "\""); + "\" referenced unknown base type \"" + tname + "\"");
} }
if (default_holder != base_info->default_holder) { if (default_holder != base_info->default_holder) {
std::string tname(base.name()); std::string tname(base.name());
detail::clean_type_id(tname); detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + pybind11_fail("generic_type: type \"" + std::string(name) + "\" "
(default_holder ? "does not have" : "has") + + (default_holder ? "does not have" : "has")
" a non-default holder type while its base \"" + tname + "\" " + + " a non-default holder type while its base \"" + tname + "\" "
(base_info->default_holder ? "does not" : "does")); + (base_info->default_holder ? "does not" : "does"));
} }
bases.append((PyObject *) base_info->type); bases.append((PyObject *) base_info->type);
@ -340,14 +355,13 @@ struct type_record {
} }
}; };
inline function_call::function_call(const function_record &f, handle p) : inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) {
func(f), parent(p) {
args.reserve(f.nargs); args.reserve(f.nargs);
args_convert.reserve(f.nargs); args_convert.reserve(f.nargs);
} }
/// Tag for a new-style `__init__` defined in `detail/init.h` /// 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 * Partial template specializations to process custom attributes provided to
@ -355,60 +369,79 @@ struct is_new_style_constructor { };
* fields in the type_record and function_record data structures or executed at * fields in the type_record and function_record data structures or executed at
* runtime to deal with custom call policies (e.g. keep_alive). * 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 /// Default implementation: do nothing
static void init(const T &, function_record *) { } static void init(const T &, function_record *) {}
static void init(const T &, type_record *) { } static void init(const T &, type_record *) {}
static void precall(function_call &) { } static void precall(function_call &) {}
static void postcall(function_call &, handle) { } static void postcall(function_call &, handle) {}
}; };
/// Process an attribute specifying the function's name /// 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); } static void init(const name &n, function_record *r) { r->name = const_cast<char *>(n.value); }
}; };
/// Process an attribute specifying the function's docstring /// 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); } 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) /// 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, function_record *r) { r->doc = const_cast<char *>(d); }
static void init(const char *d, type_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 /// 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; } 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 /// Process an attribute which indicates that this is an overloaded function associated with a
template <> struct process_attribute<sibling> : process_attribute_default<sibling> { /// given sibling
template <>
struct process_attribute<sibling> : process_attribute_default<sibling> {
static void init(const sibling &s, function_record *r) { r->sibling = s.value; } static void init(const sibling &s, function_record *r) { r->sibling = s.value; }
}; };
/// Process an attribute which indicates that this function is a method /// Process an attribute which indicates that this function is a method
template <> struct process_attribute<is_method> : process_attribute_default<is_method> { template <>
static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } 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 /// 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; } static void init(const scope &s, function_record *r) { r->scope = s.value; }
}; };
/// Process an attribute which indicates that this function is an operator /// 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; } 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> { template <>
static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } 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) { inline void check_kw_only_arg(const arg &a, function_record *r) {
@ -425,7 +458,8 @@ inline void append_self_arg_if_needed(function_record *r) {
} }
/// Process a keyword argument attribute (*without* a default value) /// 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) { static void init(const arg &a, function_record *r) {
append_self_arg_if_needed(r); append_self_arg_if_needed(r);
r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none);
@ -435,7 +469,8 @@ template <> struct process_attribute<arg> : process_attribute_default<arg> {
}; };
/// Process a keyword argument attribute (*with* a default value) /// 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) { static void init(const arg_v &a, function_record *r) {
if (r->is_method && r->args.empty()) { if (r->is_method && r->args.empty()) {
r->args.emplace_back( r->args.emplace_back(
@ -451,15 +486,16 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
descr += a.type + "'"; descr += a.type + "'";
if (r->is_method) { if (r->is_method) {
if (r->name) { 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 { } else {
descr += " in method of '" + (std::string) str(r->scope) + "'"; descr += " in method of '" + (std::string) str(r->scope) + "'";
} }
} else if (r->name) { } else if (r->name) {
descr += " in function '" + (std::string) r->name + "'"; descr += " in function '" + (std::string) r->name + "'";
} }
pybind11_fail("arg(): could not convert default argument " pybind11_fail("arg(): could not convert default argument " + descr
+ descr + " into a Python object (type not registered yet?)"); + " into a Python object (type not registered yet?)");
#else #else
pybind11_fail("arg(): could not convert default argument " pybind11_fail("arg(): could not convert default argument "
"into a Python object (type not registered yet?). " "into a Python object (type not registered yet?). "
@ -473,7 +509,8 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
}; };
/// Process a keyword-only-arguments-follow pseudo argument /// 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) { static void init(const kw_only &, function_record *r) {
append_self_arg_if_needed(r); append_self_arg_if_needed(r);
if (r->has_args && r->nargs_pos != static_cast<std::uint16_t>(r->args.size())) { if (r->has_args && r->nargs_pos != static_cast<std::uint16_t>(r->args.size())) {
@ -485,20 +522,23 @@ template <> struct process_attribute<kw_only> : process_attribute_default<kw_onl
}; };
/// Process a positional-only-argument maker /// 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) { static void init(const pos_only &, function_record *r) {
append_self_arg_if_needed(r); append_self_arg_if_needed(r);
r->nargs_pos_only = static_cast<std::uint16_t>(r->args.size()); r->nargs_pos_only = static_cast<std::uint16_t>(r->args.size());
if (r->nargs_pos_only > r->nargs_pos) { if (r->nargs_pos_only > r->nargs_pos) {
pybind11_fail("pos_only(): cannot follow a py::args() argument"); 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> 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); } static void init(const handle &h, type_record *r) { r->bases.append(h); }
}; };
@ -511,7 +551,9 @@ struct process_attribute<base<T>> : process_attribute_default<base<T>> {
/// Process a multiple inheritance attribute /// Process a multiple inheritance attribute
template <> template <>
struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> { 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 <> template <>
@ -557,34 +599,41 @@ template <>
struct process_attribute<arithmetic> : process_attribute_default<arithmetic> {}; struct process_attribute<arithmetic> : process_attribute_default<arithmetic> {};
template <typename... Ts> 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 * 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 * pre-call handler if both Nurse, Patient != 0 and use the post-call handler
* otherwise * 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> 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> 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> 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> 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 /// Recursively iterate over variadic template arguments
template <typename... Args> struct process_attributes { template <typename... Args>
static void init(const Args&... args, function_record *r) { struct process_attributes {
static void init(const Args &...args, function_record *r) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
using expander = int[]; using expander = int[];
(void) expander{ (void) expander{
0, ((void) process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...}; 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_MSVC_C4100(r);
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
using expander = int[]; using expander = int[];
@ -616,7 +665,7 @@ using extract_guard_t = typename exactly_one_t<is_call_guard, call_guard<>, Extr
/// Check the number of named arguments at compile time /// Check the number of named arguments at compile time
template <typename... Extra, template <typename... Extra,
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...), 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) { constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, 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; 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 {
void *ptr = nullptr; // Pointer to the underlying storage void *ptr = nullptr; // Pointer to the underlying storage
ssize_t itemsize = 0; // Size of individual items in bytes ssize_t itemsize = 0; // Size of individual items in bytes
ssize_t size = 0; // Total number of entries ssize_t size = 0; // Total number of entries
std::string format; // For homogeneous buffers, this should be set to std::string format; // For homogeneous buffers, this should be set to
// format_descriptor<T>::format() // format_descriptor<T>::format()
ssize_t ndim = 0; // Number of dimensions ssize_t ndim = 0; // Number of dimensions
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension) std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
std::vector<ssize_t> strides; // Number of bytes between adjacent entries std::vector<ssize_t> strides; // Number of bytes between adjacent entries
@ -54,10 +54,15 @@ struct buffer_info {
buffer_info() = default; buffer_info() = default;
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, buffer_info(void *ptr,
detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false) ssize_t itemsize,
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), const std::string &format,
shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { 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()) { if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) {
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
} }
@ -67,29 +72,48 @@ struct buffer_info {
} }
template <typename T> 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(T *ptr,
: 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) { } detail::any_container<ssize_t> shape_in,
detail::any_container<ssize_t> strides_in,
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false) bool readonly = false)
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { } : 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> template <typename T>
buffer_info(T *ptr, ssize_t size, bool readonly=false) buffer_info(T *ptr, ssize_t size, bool readonly = false)
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) { } : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) {}
template <typename T> template <typename T>
buffer_info(const T *ptr, ssize_t size, bool readonly=true) 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_cast<T *>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) {}
explicit buffer_info(Py_buffer *view, bool ownview = true) 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}, {view->shape, view->shape + view->ndim},
/* Though buffer::request() requests PyBUF_STRIDES, ctypes objects /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects
* ignore this flag and return a view with NULL strides. * ignore this flag and return a view with NULL strides.
* When strides are NULL, build them manually. */ * When strides are NULL, build them manually. */
view->strides view->strides
? std::vector<ssize_t>(view->strides, view->strides + view->ndim) ? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
(view->readonly != 0)) { (view->readonly != 0)) {
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
this->m_view = view; this->m_view = view;
@ -98,7 +122,7 @@ struct buffer_info {
} }
buffer_info(const buffer_info &) = delete; 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); } buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }
@ -117,17 +141,28 @@ struct buffer_info {
} }
~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() const { return m_view; }
Py_buffer *&view() { 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, private:
detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in, bool readonly) struct private_ctr_tag {};
: buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { }
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; Py_buffer *m_view = nullptr;
bool ownview = false; bool ownview = false;
@ -135,17 +170,22 @@ private:
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <typename T, typename SFINAE = void> struct compare_buffer_info { template <typename T, typename SFINAE = void>
static bool compare(const buffer_info& b) { struct compare_buffer_info {
static bool compare(const buffer_info &b) {
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T); 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>> { template <typename T>
static bool compare(const buffer_info& b) { struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor<T>::value || static bool compare(const buffer_info &b) {
((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned<T>::value ? "L" : "l")) || return (size_t) b.itemsize == sizeof(T)
((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned<T>::value ? "N" : "n"))); && (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 @@
#include <chrono> #include <chrono>
#include <cmath> #include <cmath>
#include <ctime> #include <ctime>
#include <mutex>
#include <datetime.h> #include <datetime.h>
#include <mutex>
// Backport the PyDateTime_DELTA functions from Python3.3 if required // Backport the PyDateTime_DELTA functions from Python3.3 if required
#ifndef PyDateTime_DELTA_GET_DAYS #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 #endif
#ifndef PyDateTime_DELTA_GET_SECONDS #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 #endif
#ifndef PyDateTime_DELTA_GET_MICROSECONDS #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 #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <typename type> class duration_caster { template <typename type>
class duration_caster {
public: public:
using rep = typename type::rep; using rep = typename type::rep;
using period = typename type::period; using period = typename type::period;
@ -45,7 +45,9 @@ public:
using namespace std::chrono; using namespace std::chrono;
// Lazy initialise the PyDateTime import // Lazy initialise the PyDateTime import
if (!PyDateTimeAPI) { PyDateTime_IMPORT; } if (!PyDateTimeAPI) {
PyDateTime_IMPORT;
}
if (!src) { if (!src) {
return false; return false;
@ -53,26 +55,30 @@ public:
// If invoked with datetime.delta object // If invoked with datetime.delta object
if (PyDelta_Check(src.ptr())) { if (PyDelta_Check(src.ptr())) {
value = type(duration_cast<duration<rep, period>>( 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())) + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
+ microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
return true; return true;
} }
// If invoked with a float we assume it is seconds and convert // If invoked with a float we assume it is seconds and convert
if (PyFloat_Check(src.ptr())) { 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 true;
} }
return false; return false;
} }
// If this is a duration just return it back // 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; return src;
} }
// If this is a time_point get the time_since_epoch // 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(); return src.time_since_epoch();
} }
@ -84,9 +90,12 @@ public:
auto d = get_duration(src); auto d = get_duration(src);
// Lazy initialise the PyDateTime import // 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 dd_t = duration<int, std::ratio<86400>>;
using ss_t = duration<int, std::ratio<1>>; using ss_t = duration<int, std::ratio<1>>;
using us_t = duration<int, std::micro>; using us_t = duration<int, std::micro>;
@ -118,14 +127,17 @@ inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
} }
// This is for casting times on the system clock into datetime.datetime instances // 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: public:
using type = std::chrono::time_point<std::chrono::system_clock, Duration>; using type = std::chrono::time_point<std::chrono::system_clock, Duration>;
bool load(handle src, bool) { bool load(handle src, bool) {
using namespace std::chrono; using namespace std::chrono;
// Lazy initialise the PyDateTime import // Lazy initialise the PyDateTime import
if (!PyDateTimeAPI) { PyDateTime_IMPORT; } if (!PyDateTimeAPI) {
PyDateTime_IMPORT;
}
if (!src) { if (!src) {
return false; return false;
@ -135,32 +147,32 @@ public:
microseconds msecs; microseconds msecs;
if (PyDateTime_Check(src.ptr())) { if (PyDateTime_Check(src.ptr())) {
cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
cal.tm_isdst = -1; 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())) { } else if (PyDate_Check(src.ptr())) {
cal.tm_sec = 0; cal.tm_sec = 0;
cal.tm_min = 0; cal.tm_min = 0;
cal.tm_hour = 0; cal.tm_hour = 0;
cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
cal.tm_isdst = -1; cal.tm_isdst = -1;
msecs = microseconds(0); msecs = microseconds(0);
} else if (PyTime_Check(src.ptr())) { } else if (PyTime_Check(src.ptr())) {
cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr()); cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr()); cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
cal.tm_hour = PyDateTime_TIME_GET_HOUR(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_mday = 1; // This date (day, month, year) = (1, 0, 70)
cal.tm_mon = 0; // represents 1-Jan-1970, which is the first 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_year = 70; // earliest available date for Python's datetime
cal.tm_isdst = -1; cal.tm_isdst = -1;
msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr())); msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
} else { } else {
return false; return false;
} }
@ -169,14 +181,18 @@ public:
return true; 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; using namespace std::chrono;
// Lazy initialise the PyDateTime import // 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 // Get out microseconds, and make sure they are positive, to avoid bug in eastern
// (cfr. https://github.com/pybind/pybind11/issues/2417) // hemisphere time zones (cfr. https://github.com/pybind/pybind11/issues/2417)
using us_t = duration<int, std::micro>; using us_t = duration<int, std::micro>;
auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1)); auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1));
if (us.count() < 0) { if (us.count() < 0) {
@ -184,9 +200,10 @@ public:
} }
// Subtract microseconds BEFORE `system_clock::to_time_t`, because: // 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. // > If std::time_t has lower precision, it is implementation-defined whether the value is
// (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t) // 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::time_t tt
= system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
std::tm localtime; std::tm localtime;
std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime); std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
@ -207,13 +224,13 @@ public:
// Other clocks that are not the system clock are not measured as datetime.datetime objects // 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 // 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 // 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>> template <typename Clock, typename Duration>
: public duration_caster<std::chrono::time_point<Clock, 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>> template <typename Rep, typename Period>
: public duration_caster<std::chrono::duration<Rep, Period>> { class type_caster<std::chrono::duration<Rep, Period>>
}; : public duration_caster<std::chrono::duration<Rep, Period>> {};
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

23
include/pybind11/complex.h

@ -10,36 +10,42 @@
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include <complex> #include <complex>
/// glibc defines I as a macro which breaks things, e.g., boost template names /// glibc defines I as a macro which breaks things, e.g., boost template names
#ifdef I #ifdef I
# undef I # undef I
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 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 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); } static std::string format() { return std::string(value); }
}; };
#ifndef PYBIND11_CPP17 #ifndef PYBIND11_CPP17
template <typename T> constexpr const char format_descriptor< template <typename T>
std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>>::value[3]; constexpr const char
format_descriptor<std::complex<T>,
detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
#endif #endif
PYBIND11_NAMESPACE_BEGIN(detail) 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 bool value = true;
static constexpr int index = is_fmt_numeric<T>::index + 3; 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: public:
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
if (!src) { if (!src) {
@ -57,7 +63,8 @@ public:
return true; 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()); return PyComplex_FromDoubles((double) src.real(), (double) src.imag());
} }

66
include/pybind11/detail/class.h

@ -16,12 +16,13 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION) #if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION)
# define PYBIND11_BUILTIN_QUALNAME # define PYBIND11_BUILTIN_QUALNAME
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
#else #else
// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type // 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: // 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 #endif
inline std::string get_fully_qualified_tp_name(PyTypeObject *type) { inline std::string get_fully_qualified_tp_name(PyTypeObject *type) {
@ -71,9 +72,9 @@ inline PyTypeObject *make_static_property_type() {
} }
heap_type->ht_name = name_obj.inc_ref().ptr(); 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(); heap_type->ht_qualname = name_obj.inc_ref().ptr();
#endif # endif
auto *type = &heap_type->ht_type; auto *type = &heap_type->ht_type;
type->tp_name = name; type->tp_name = name;
@ -107,8 +108,10 @@ inline PyTypeObject *make_static_property_type() {
def __set__(self, obj, value): def __set__(self, obj, value):
cls = obj if isinstance(obj, type) else type(obj) cls = obj if isinstance(obj, type) else type(obj)
property.__set__(self, cls, value) property.__set__(self, cls, value)
)", Py_file_input, d.ptr(), d.ptr() )",
); Py_file_input,
d.ptr(),
d.ptr());
if (result == nullptr) if (result == nullptr)
throw error_already_set(); throw error_already_set();
Py_DECREF(result); Py_DECREF(result);
@ -121,7 +124,7 @@ inline PyTypeObject *make_static_property_type() {
By default, Python replaces the `static_property` itself, but for wrapped C++ types 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 we need to call `static_property.__set__()` in order to propagate the new value to
the underlying C++ data structure. */ 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 // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
@ -184,7 +187,8 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
// Ensure that the base __init__ function(s) were called // Ensure that the base __init__ function(s) were called
for (const auto &vh : values_and_holders(instance)) { for (const auto &vh : values_and_holders(instance)) {
if (!vh.holder_constructed()) { 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()); get_fully_qualified_tp_name(vh.type->type).c_str());
Py_DECREF(self); Py_DECREF(self);
return nullptr; return nullptr;
@ -203,9 +207,8 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
// 1) be found in internals.registered_types_py // 1) be found in internals.registered_types_py
// 2) have exactly one associated `detail::type_info` // 2) have exactly one associated `detail::type_info`
auto found_type = internals.registered_types_py.find(type); auto found_type = internals.registered_types_py.find(type);
if (found_type != internals.registered_types_py.end() && if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1
found_type->second.size() == 1 && && found_type->second[0]->type == type) {
found_type->second[0]->type == type) {
auto *tinfo = found_type->second[0]; auto *tinfo = found_type->second[0];
auto tindex = std::type_index(*tinfo->cpptype); auto tindex = std::type_index(*tinfo->cpptype);
@ -220,7 +223,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
// Actually just `std::erase_if`, but that's only available in C++20 // Actually just `std::erase_if`, but that's only available in C++20
auto &cache = internals.inactive_override_cache; 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) { if (it->first == (PyObject *) tinfo->type) {
it = cache.erase(it); it = cache.erase(it);
} else { } else {
@ -237,7 +240,7 @@ extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
/** This metaclass is assigned by default to all pybind11 types and is required in order /** 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`. for static properties to function correctly. Users may override this using `py::metaclass`.
Return value: New reference. */ Return value: New reference. */
inline PyTypeObject* make_default_metaclass() { inline PyTypeObject *make_default_metaclass() {
constexpr auto *name = "pybind11_type"; constexpr auto *name = "pybind11_type";
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name)); auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
@ -281,9 +284,12 @@ inline PyTypeObject* make_default_metaclass() {
/// For multiple inheritance types we need to recursively register/deregister base pointers for any /// 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 /// 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. /// correctly recognize an offset base class pointer. This calls a function with any offset base
inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, /// ptrs.
bool (*f)(void * /*parentptr*/, instance * /*self*/)) { 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)) { for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) {
if (auto *parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { if (auto *parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) {
for (auto &c : parent_tinfo->implicit_casts) { for (auto &c : parent_tinfo->implicit_casts) {
@ -331,13 +337,13 @@ inline bool deregister_instance(instance *self, void *valptr, const type_info *t
return ret; return ret;
} }
/// Instance creation function for all pybind11 types. It allocates the internal instance layout for /// Instance creation function for all pybind11 types. It allocates the internal instance layout
/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast /// for holding C++ objects and holders. Allocation is done lazily (the first time the instance is
/// to a reference or pointer), and initialization is done by an `__init__` function. /// cast to a reference or pointer), and initialization is done by an `__init__` function.
inline PyObject *make_new_instance(PyTypeObject *type) { inline PyObject *make_new_instance(PyTypeObject *type) {
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first
// object is a plain Python type (i.e. not derived from an extension type). Fix it. // 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)); ssize_t instance_size = static_cast<ssize_t>(sizeof(instance));
if (type->tp_basicsize < instance_size) { if (type->tp_basicsize < instance_size) {
type->tp_basicsize = instance_size; type->tp_basicsize = instance_size;
@ -511,7 +517,8 @@ extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
/// dynamic_attr: Support for `instance.__dict__ = dict()`. /// dynamic_attr: Support for `instance.__dict__ = dict()`.
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
if (!PyDict_Check(new_dict)) { 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()); get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str());
return -1; return -1;
} }
@ -540,15 +547,14 @@ extern "C" inline int pybind11_clear(PyObject *self) {
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
auto *type = &heap_type->ht_type; auto *type = &heap_type->ht_type;
type->tp_flags |= Py_TPFLAGS_HAVE_GC; type->tp_flags |= Py_TPFLAGS_HAVE_GC;
type->tp_dictoffset = type->tp_basicsize; // place dict at the end 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_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
type->tp_traverse = pybind11_traverse; type->tp_traverse = pybind11_traverse;
type->tp_clear = pybind11_clear; type->tp_clear = pybind11_clear;
static PyGetSetDef getset[] = { static PyGetSetDef getset[] = {
{const_cast<char*>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, {const_cast<char *>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr} {nullptr, nullptr, nullptr, nullptr, nullptr}};
};
type->tp_getset = getset; type->tp_getset = getset;
} }
@ -617,7 +623,7 @@ inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
/** Create a brand new Python type according to the `type_record` specification. /** Create a brand new Python type according to the `type_record` specification.
Return value: New reference. */ 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 name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name));
auto qualname = name; auto qualname = name;
@ -678,7 +684,7 @@ inline PyObject* make_new_python_type(const type_record &rec) {
auto *type = &heap_type->ht_type; auto *type = &heap_type->ht_type;
type->tp_name = full_name; type->tp_name = full_name;
type->tp_doc = tp_doc; 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)); type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
if (!bases.empty()) { if (!bases.empty()) {
type->tp_bases = bases.release().ptr(); 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)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if !defined(_MSC_VER) #if !defined(_MSC_VER)
# define PYBIND11_DESCR_CONSTEXPR static constexpr # define PYBIND11_DESCR_CONSTEXPR static constexpr
#else #else
# define PYBIND11_DESCR_CONSTEXPR const # define PYBIND11_DESCR_CONSTEXPR const
#endif #endif
/* Concatenate type signatures at compile time */ /* Concatenate type signatures at compile time */
@ -27,14 +27,14 @@ struct descr {
constexpr descr() = default; constexpr descr() = default;
// NOLINTNEXTLINE(google-explicit-constructor) // 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> 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> template <typename... Chars>
// NOLINTNEXTLINE(google-explicit-constructor) // 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() { static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() {
return {{&typeid(Ts)..., nullptr}}; return {{&typeid(Ts)..., nullptr}};
@ -42,81 +42,106 @@ struct descr {
}; };
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2> 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, constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a,
index_sequence<Is1...>, index_sequence<Is2...>) { const descr<N2, Ts2...> &b,
index_sequence<Is1...>,
index_sequence<Is2...>) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b);
return {a.text[Is1]..., b.text[Is2]...}; return {a.text[Is1]..., b.text[Is2]...};
} }
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2> 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>()); return plus_impl(a, b, make_index_sequence<N1>(), make_index_sequence<N2>());
} }
template <size_t N> template <size_t N>
constexpr descr<N - 1> const_name(char const(&text)[N]) { return descr<N - 1>(text); } constexpr descr<N - 1> const_name(char const (&text)[N]) {
constexpr descr<0> const_name(char const(&)[1]) { return {}; } 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 Rem, size_t... Digits>
template <size_t...Digits> struct int_to_str<0, 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. // WARNING: This only works with C++17 or higher.
static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...); static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...);
}; };
// Ternary description (like std::conditional) // Ternary description (like std::conditional)
template <bool B, size_t N1, size_t N2> 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); return const_name(text1);
} }
template <bool B, size_t N1, size_t N2> 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); return const_name(text2);
} }
template <bool B, typename T1, typename T2> 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> 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> template <size_t Size>
auto constexpr const_name() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> { 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; 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. // If "_" is defined as a macro, py::detail::_ cannot be provided.
// It is therefore best to use py::detail::const_name universally. // It is therefore best to use py::detail::const_name universally.
// This block is for backward compatibility only. // This block is for backward compatibility only.
// (The const_name code is repeated to avoid introducing a "_" #define ourselves.) // (The const_name code is repeated to avoid introducing a "_" #define ourselves.)
#ifndef _ #ifndef _
#define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY # define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
template <size_t N> 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> 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]) { constexpr enable_if_t<B, descr<N1 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
return const_name<B,N1,N2>(text1, text2); return const_name<B, N1, N2>(text1, text2);
} }
template <bool B, size_t N1, size_t N2> 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]) { constexpr enable_if_t<!B, descr<N2 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
return const_name<B,N1,N2>(text1, text2); return const_name<B, N1, N2>(text1, text2);
} }
template <bool B, typename T1, typename T2> 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> 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> template <size_t Size>
auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> { auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
return const_name<Size>(); return const_name<Size>();
} }
template <typename Type> constexpr descr<1, Type> _() { return const_name<Type>(); } template <typename Type>
#endif // #ifndef _ constexpr descr<1, Type> _() {
return const_name<Type>();
}
#endif // #ifndef _
constexpr descr<0> concat() { return {}; } constexpr descr<0> concat() { return {}; }
template <size_t N, typename... Ts> 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> template <size_t N, typename... Ts, typename... Args>
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args) constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)

238
include/pybind11/detail/init.h

@ -22,7 +22,8 @@ public:
return true; 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; } explicit operator value_and_holder &() { return *value; }
static constexpr auto name = const_name<value_and_holder>(); static constexpr auto name = const_name<value_and_holder>();
@ -39,11 +40,15 @@ inline void no_nullptr(void *ptr) {
} }
// Implementing functions for all forms of py::init<...> and py::init(...) // Implementing functions for all forms of py::init<...> and py::init(...)
template <typename Class> using Cpp = typename Class::type; template <typename Class>
template <typename Class> using Alias = typename Class::type_alias; using Cpp = typename Class::type;
template <typename Class> using Holder = typename Class::holder_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. // 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> template <typename Class, enable_if_t<Class::has_alias, int> = 0>
@ -52,17 +57,27 @@ bool is_alias(Cpp<Class> *ptr) {
} }
// Failing fallback version of the above for a no-alias class (always returns false) // Failing fallback version of the above for a no-alias class (always returns false)
template <typename /*Class*/> 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 // 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 // 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 // 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 // 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). // 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> template <typename Class,
inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward<Args>(args)...); } typename... Args,
template <typename Class, typename... Args, detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0> 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)...}; } 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 // 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 // an alias to provide only a single Cpp factory function as long as the Alias can be
@ -71,12 +86,14 @@ inline Class *construct_or_initialize(Args &&...args) { return new Class{std::fo
// inherit all the base class constructors. // inherit all the base class constructors.
template <typename Class> template <typename Class>
void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, 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)); v_h.value_ptr() = new Alias<Class>(std::move(base));
} }
template <typename Class> template <typename Class>
[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, [[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 " throw type_error("pybind11::init(): unable to convert returned instance to required "
"alias class: no `Alias<Class>(Class &&)` constructor available"); "alias class: no `Alias<Class>(Class &&)` constructor available");
} }
@ -86,8 +103,8 @@ template <typename Class>
template <typename Class> template <typename Class>
void construct(...) { void construct(...) {
static_assert(!std::is_same<Class, Class>::value /* always false */, static_assert(!std::is_same<Class, Class>::value /* always false */,
"pybind11::init(): init function must return a compatible pointer, " "pybind11::init(): init function must return a compatible pointer, "
"holder, or value"); "holder, or value");
} }
// Pointer return v1: the factory function returns a class pointer for a registered class. // Pointer return v1: the factory function returns a class pointer for a registered class.
@ -108,7 +125,7 @@ void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
// the holder and destruction happens when we leave the C++ scope, and the holder // the holder and destruction happens when we leave the C++ scope, and the holder
// class gets to handle the destruction however it likes. // class gets to handle the destruction however it likes.
v_h.value_ptr() = ptr; 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 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 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 v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null
@ -131,7 +148,8 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
// Holder return: copy its pointer, and move or copy the returned holder into the new instance's // Holder 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 // 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> template <typename Class>
void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
@ -155,7 +173,7 @@ template <typename Class>
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) { void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
static_assert(std::is_move_constructible<Cpp<Class>>::value, 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) { if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias) {
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result)); construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
} else { } else {
@ -168,7 +186,8 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
// cases where Alias initialization is always desired. // cases where Alias initialization is always desired.
template <typename Class> template <typename Class>
void construct(value_and_holder &v_h, Alias<Class> &&result, bool) { 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"); "pybind11::init() return-by-alias-value factory function requires a movable alias class");
v_h.value_ptr() = new Alias<Class>(std::move(result)); v_h.value_ptr() = new Alias<Class>(std::move(result));
} }
@ -177,16 +196,21 @@ void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
template <typename... Args> template <typename... Args>
struct constructor { struct constructor {
template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0> template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0>
static void execute(Class &cl, const Extra&... extra) { static void execute(Class &cl, const Extra &...extra) {
cl.def("__init__", [](value_and_holder &v_h, Args... args) { cl.def(
v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...); "__init__",
}, is_new_style_constructor(), extra...); [](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, template <typename Class,
enable_if_t<Class::has_alias && typename... Extra,
std::is_constructible<Cpp<Class>, Args...>::value, int> = 0> enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value,
static void execute(Class &cl, const Extra&... extra) { int> = 0>
static void execute(Class &cl, const Extra &...extra) {
cl.def( cl.def(
"__init__", "__init__",
[](value_and_holder &v_h, Args... args) { [](value_and_holder &v_h, Args... args) {
@ -202,30 +226,46 @@ struct constructor {
extra...); extra...);
} }
template <typename Class, typename... Extra, template <typename Class,
enable_if_t<Class::has_alias && typename... Extra,
!std::is_constructible<Cpp<Class>, Args...>::value, int> = 0> enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value,
static void execute(Class &cl, const Extra&... extra) { int> = 0>
cl.def("__init__", [](value_and_holder &v_h, Args... args) { static void execute(Class &cl, const Extra &...extra) {
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); cl.def(
}, is_new_style_constructor(), extra...); "__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<...>() // Implementing class for py::init_alias<...>()
template <typename... Args> struct alias_constructor { template <typename... Args>
template <typename Class, typename... Extra, struct alias_constructor {
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int> = 0> template <typename Class,
static void execute(Class &cl, const Extra&... extra) { typename... Extra,
cl.def("__init__", [](value_and_holder &v_h, Args... args) { enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value,
v_h.value_ptr() = construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...); int> = 0>
}, 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<Alias<Class>>(std::forward<Args>(args)...);
},
is_new_style_constructor(),
extra...);
} }
}; };
// Implementation class for py::init(Func) and py::init(Func, AliasFunc) // Implementation class for py::init(Func) and py::init(Func, AliasFunc)
template <typename CFunc, typename AFunc = void_type (*)(), template <typename CFunc,
typename = function_signature_t<CFunc>, typename = function_signature_t<AFunc>> typename AFunc = void_type (*)(),
typename = function_signature_t<CFunc>,
typename = function_signature_t<AFunc>>
struct factory; struct factory;
// Specialization for py::init(Func) // Specialization for py::init(Func)
@ -243,22 +283,32 @@ struct factory<Func, void_type (*)(), Return(Args...)> {
// instance, or the alias needs to be constructible from a `Class &&` argument. // instance, or the alias needs to be constructible from a `Class &&` argument.
template <typename Class, typename... Extra> template <typename Class, typename... Extra>
void execute(Class &cl, const Extra &...extra) && { void execute(Class &cl, const Extra &...extra) && {
#if defined(PYBIND11_CPP14) #if defined(PYBIND11_CPP14)
cl.def("__init__", [func = std::move(class_factory)] cl.def(
#else "__init__",
[func = std::move(class_factory)]
#else
auto &func = class_factory; auto &func = class_factory;
cl.def("__init__", [func] cl.def(
#endif "__init__",
(value_and_holder &v_h, Args... args) { [func]
construct<Class>(v_h, func(std::forward<Args>(args)...), #endif
Py_TYPE(v_h.inst) != v_h.type->type); (value_and_holder &v_h, Args... args) {
}, is_new_style_constructor(), extra...); 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) // Specialization for py::init(Func, AliasFunc)
template <typename CFunc, typename AFunc, template <typename CFunc,
typename CReturn, typename... CArgs, typename AReturn, typename... AArgs> typename AFunc,
typename CReturn,
typename... CArgs,
typename AReturn,
typename... AArgs>
struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> { struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
static_assert(sizeof...(CArgs) == sizeof...(AArgs), static_assert(sizeof...(CArgs) == sizeof...(AArgs),
"pybind11::init(class_factory, alias_factory): class and alias factories " "pybind11::init(class_factory, alias_factory): class and alias factories "
@ -271,30 +321,37 @@ struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
remove_reference_t<AFunc> alias_factory; remove_reference_t<AFunc> alias_factory;
factory(CFunc &&c, AFunc &&a) 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 // 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. // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype.
template <typename Class, typename... Extra> template <typename Class, typename... Extra>
void execute(Class &cl, const Extra&... extra) && { void execute(Class &cl, const Extra &...extra) && {
static_assert(Class::has_alias, "The two-argument version of `py::init()` can " static_assert(Class::has_alias,
"only be used if the class has an alias"); "The two-argument version of `py::init()` can "
#if defined(PYBIND11_CPP14) "only be used if the class has an alias");
cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] #if defined(PYBIND11_CPP14)
#else cl.def(
"__init__",
[class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
#else
auto &class_func = class_factory; auto &class_func = class_factory;
auto &alias_func = alias_factory; auto &alias_func = alias_factory;
cl.def("__init__", [class_func, alias_func] cl.def(
#endif "__init__",
(value_and_holder &v_h, CArgs... args) { [class_func, alias_func]
if (Py_TYPE(v_h.inst) == v_h.type->type) { #endif
// If the instance type equals the registered type we don't have inheritance, so (value_and_holder &v_h, CArgs... args) {
// don't need the alias and can construct using the class function: if (Py_TYPE(v_h.inst) == v_h.type->type) {
construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false); // If the instance type equals the registered type we don't have inheritance,
} else { // so don't need the alias and can construct using the class function:
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true); construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
} } else {
}, is_new_style_constructor(), extra...); construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
}
},
is_new_style_constructor(),
extra...);
} }
}; };
@ -305,7 +362,9 @@ void setstate(value_and_holder &v_h, T &&result, bool need_alias) {
} }
/// Set both the C++ and Python states /// 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> 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) { 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); construct<Class>(v_h, std::move(result.first), need_alias);
@ -319,12 +378,18 @@ void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias)
} }
/// Implementation for py::pickle(GetState, SetState) /// Implementation for py::pickle(GetState, SetState)
template <typename Get, typename Set, template <typename Get,
typename = function_signature_t<Get>, typename = function_signature_t<Set>> typename Set,
typename = function_signature_t<Get>,
typename = function_signature_t<Set>>
struct pickle_factory; struct pickle_factory;
template <typename Get, typename Set, template <typename Get,
typename RetState, typename Self, typename NewInstance, typename ArgState> typename Set,
typename RetState,
typename Self,
typename NewInstance,
typename ArgState>
struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> { struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value, static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value,
"The type returned by `__getstate__` must be the same " "The type returned by `__getstate__` must be the same "
@ -333,23 +398,28 @@ struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
remove_reference_t<Get> get; remove_reference_t<Get> get;
remove_reference_t<Set> set; remove_reference_t<Set> set;
pickle_factory(Get get, Set set) pickle_factory(Get get, Set set) : get(std::forward<Get>(get)), set(std::forward<Set>(set)) {}
: get(std::forward<Get>(get)), set(std::forward<Set>(set)) { }
template <typename Class, typename... Extra> template <typename Class, typename... Extra>
void execute(Class &cl, const Extra &...extra) && { void execute(Class &cl, const Extra &...extra) && {
cl.def("__getstate__", std::move(get)); cl.def("__getstate__", std::move(get));
#if defined(PYBIND11_CPP14) #if defined(PYBIND11_CPP14)
cl.def("__setstate__", [func = std::move(set)] cl.def(
"__setstate__",
[func = std::move(set)]
#else #else
auto &func = set; auto &func = set;
cl.def("__setstate__", [func] cl.def(
"__setstate__",
[func]
#endif #endif
(value_and_holder &v_h, ArgState state) { (value_and_holder &v_h, ArgState state) {
setstate<Class>(v_h, func(std::forward<ArgState>(state)), setstate<Class>(
Py_TYPE(v_h.inst) != v_h.type->type); v_h, func(std::forward<ArgState>(state)), Py_TYPE(v_h.inst) != v_h.type->type);
}, is_new_style_constructor(), extra...); },
is_new_style_constructor(),
extra...);
} }
}; };

115
include/pybind11/detail/internals.h

@ -10,6 +10,7 @@
#pragma once #pragma once
#include "../pytypes.h" #include "../pytypes.h"
#include <exception> #include <exception>
/// Tracks the `internals` and `type_info` ABI version independent of the main library version. /// Tracks the `internals` and `type_info` ABI version independent of the main library version.
@ -136,9 +137,9 @@ template <typename value_type>
using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>; using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>;
struct override_hash { 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); 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; return value;
} }
}; };
@ -151,8 +152,9 @@ struct internals {
type_map<type_info *> registered_types_cpp; type_map<type_info *> registered_types_cpp;
// PyTypeObject* -> base type_info(s) // PyTypeObject* -> base type_info(s)
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py;
std::unordered_multimap<const void *, instance*> registered_instances; // void * -> instance* 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_set<std::pair<const PyObject *, const char *>, override_hash>
inactive_override_cache;
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions; type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
std::unordered_map<const PyObject *, std::vector<PyObject *>> patients; std::unordered_map<const PyObject *, std::vector<PyObject *>> patients;
std::forward_list<ExceptionTranslator> registered_exception_translators; std::forward_list<ExceptionTranslator> registered_exception_translators;
@ -198,8 +200,8 @@ struct type_info {
void *(*operator_new)(size_t); void *(*operator_new)(size_t);
void (*init_instance)(instance *, const void *); void (*init_instance)(instance *, const void *);
void (*dealloc)(value_and_holder &v_h); void (*dealloc)(value_and_holder &v_h);
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions; std::vector<PyObject *(*) (PyObject *, PyTypeObject *)> implicit_conversions;
std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts; std::vector<std::pair<const std::type_info *, void *(*) (void *)>> implicit_casts;
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions; std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;
buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; buffer_info *(*get_buffer)(PyObject *, void *) = nullptr;
void *get_buffer_data = nullptr; void *get_buffer_data = nullptr;
@ -219,67 +221,71 @@ struct type_info {
/// On MSVC, debug and release builds are not ABI-compatible! /// On MSVC, debug and release builds are not ABI-compatible!
#if defined(_MSC_VER) && defined(_DEBUG) #if defined(_MSC_VER) && defined(_DEBUG)
# define PYBIND11_BUILD_TYPE "_debug" # define PYBIND11_BUILD_TYPE "_debug"
#else #else
# define PYBIND11_BUILD_TYPE "" # define PYBIND11_BUILD_TYPE ""
#endif #endif
/// Let's assume that different compilers are ABI-incompatible. /// Let's assume that different compilers are ABI-incompatible.
/// A user can manually set this string if they know their /// A user can manually set this string if they know their
/// compiler is compatible. /// compiler is compatible.
#ifndef PYBIND11_COMPILER_TYPE #ifndef PYBIND11_COMPILER_TYPE
# if defined(_MSC_VER) # if defined(_MSC_VER)
# define PYBIND11_COMPILER_TYPE "_msvc" # define PYBIND11_COMPILER_TYPE "_msvc"
# elif defined(__INTEL_COMPILER) # elif defined(__INTEL_COMPILER)
# define PYBIND11_COMPILER_TYPE "_icc" # define PYBIND11_COMPILER_TYPE "_icc"
# elif defined(__clang__) # elif defined(__clang__)
# define PYBIND11_COMPILER_TYPE "_clang" # define PYBIND11_COMPILER_TYPE "_clang"
# elif defined(__PGI) # elif defined(__PGI)
# define PYBIND11_COMPILER_TYPE "_pgi" # define PYBIND11_COMPILER_TYPE "_pgi"
# elif defined(__MINGW32__) # elif defined(__MINGW32__)
# define PYBIND11_COMPILER_TYPE "_mingw" # define PYBIND11_COMPILER_TYPE "_mingw"
# elif defined(__CYGWIN__) # elif defined(__CYGWIN__)
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" # define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
# elif defined(__GNUC__) # elif defined(__GNUC__)
# define PYBIND11_COMPILER_TYPE "_gcc" # define PYBIND11_COMPILER_TYPE "_gcc"
# else # else
# define PYBIND11_COMPILER_TYPE "_unknown" # define PYBIND11_COMPILER_TYPE "_unknown"
# endif # endif
#endif #endif
/// Also standard libs /// Also standard libs
#ifndef PYBIND11_STDLIB #ifndef PYBIND11_STDLIB
# if defined(_LIBCPP_VERSION) # if defined(_LIBCPP_VERSION)
# define PYBIND11_STDLIB "_libcpp" # define PYBIND11_STDLIB "_libcpp"
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__) # elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
# define PYBIND11_STDLIB "_libstdcpp" # define PYBIND11_STDLIB "_libstdcpp"
# else # else
# define PYBIND11_STDLIB "" # define PYBIND11_STDLIB ""
# endif # endif
#endif #endif
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. /// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
#ifndef PYBIND11_BUILD_ABI #ifndef PYBIND11_BUILD_ABI
# if defined(__GXX_ABI_VERSION) # if defined(__GXX_ABI_VERSION)
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) # define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
# else # else
# define PYBIND11_BUILD_ABI "" # define PYBIND11_BUILD_ABI ""
# endif # endif
#endif #endif
#ifndef PYBIND11_INTERNALS_KIND #ifndef PYBIND11_INTERNALS_KIND
# if defined(WITH_THREAD) # if defined(WITH_THREAD)
# define PYBIND11_INTERNALS_KIND "" # define PYBIND11_INTERNALS_KIND ""
# else # else
# define PYBIND11_INTERNALS_KIND "_without_thread" # define PYBIND11_INTERNALS_KIND "_without_thread"
# endif # endif
#endif #endif
#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ #define PYBIND11_INTERNALS_ID \
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" "__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" \ #define PYBIND11_MODULE_LOCAL_ID \
PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" "__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 /// Each module locally stores a pointer to the `internals` data. The data
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
@ -396,8 +402,12 @@ inline void translate_local_exception(std::exception_ptr p) {
if (p) { if (p) {
std::rethrow_exception(p); std::rethrow_exception(p);
} }
} catch (error_already_set &e) { e.restore(); return; } catch (error_already_set &e) {
} catch (const builtin_exception &e) { e.set_error(); return; e.restore();
return;
} catch (const builtin_exception &e) {
e.set_error();
return;
} }
} }
#endif #endif
@ -412,7 +422,7 @@ PYBIND11_NOINLINE internals &get_internals() {
// Ensure that the GIL is held since we will need to make Python calls. // 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. // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
struct gil_scoped_acquire_local { 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); } ~gil_scoped_acquire_local() { PyGILState_Release(state); }
const PyGILState_STATE state; const PyGILState_STATE state;
} gil; } gil;
@ -512,11 +522,10 @@ struct local_internals {
/// Works like `get_internals`, but for things which are locally registered. /// Works like `get_internals`, but for things which are locally registered.
inline local_internals &get_local_internals() { inline local_internals &get_local_internals() {
static local_internals locals; static local_internals locals;
return locals; return locals;
} }
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its /// 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 /// `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 /// cleared when the program exits or after interpreter shutdown (when embedding), and so are
@ -548,7 +557,7 @@ PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if /// 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 /// 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. /// 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) { T &get_or_create_shared_data(const std::string &name) {
auto &internals = detail::get_internals(); auto &internals = detail::get_internals();
auto it = internals.shared_data.find(name); auto it = internals.shared_data.find(name);

309
include/pybind11/detail/type_caster_base.h

@ -14,6 +14,7 @@
#include "descr.h" #include "descr.h"
#include "internals.h" #include "internals.h"
#include "typeid.h" #include "typeid.h"
#include <cstdint> #include <cstdint>
#include <iterator> #include <iterator>
#include <new> #include <new>
@ -32,7 +33,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
/// Adding a patient will keep it alive up until the enclosing function returns. /// Adding a patient will keep it alive up until the enclosing function returns.
class loader_life_support { class loader_life_support {
private: private:
loader_life_support* parent = nullptr; loader_life_support *parent = nullptr;
std::unordered_set<PyObject *> keep_alive; std::unordered_set<PyObject *> keep_alive;
#if defined(WITH_THREAD) #if defined(WITH_THREAD)
@ -98,7 +99,8 @@ public:
// Gets the cache entry for the given type, creating it if necessary. The return value is the pair // 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 // returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was
// just created. // 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. // Populates a just-created cache entry.
PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) { PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) {
@ -119,23 +121,25 @@ PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_
auto it = type_dict.find(type); auto it = type_dict.find(type);
if (it != type_dict.end()) { if (it != type_dict.end()) {
// We found a cache entry for it, so it's either pybind-registered or has pre-computed // 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 // pybind bases, but we have to make sure we haven't already seen the type(s) before:
// want to follow Python/virtual C++ rules that there should only be one instance of a // we want to follow Python/virtual C++ rules that there should only be one instance of
// common base. // a common base.
for (auto *tinfo : it->second) { for (auto *tinfo : it->second) {
// NB: Could use a second set here, rather than doing a linear search, but since // 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 // having a large number of immediate pybind11-registered types seems fairly
// unlikely, that probably isn't worthwhile. // unlikely, that probably isn't worthwhile.
bool found = false; bool found = false;
for (auto *known : bases) { for (auto *known : bases) {
if (known == tinfo) { found = true; break; } if (known == tinfo) {
found = true;
break;
}
} }
if (!found) { if (!found) {
bases.push_back(tinfo); 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 // It's some python type, so keep follow its bases classes to look for one or more
// registered types // registered types
if (i + 1 == check.size()) { if (i + 1 == check.size()) {
@ -177,7 +181,7 @@ inline const std::vector<detail::type_info *> &all_type_info(PyTypeObject *type)
* ancestors are pybind11-registered. Throws an exception if there are multiple bases--use * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use
* `all_type_info` instead if you want to support multiple bases. * `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); const auto &bases = all_type_info(type);
if (bases.empty()) { if (bases.empty()) {
return nullptr; return nullptr;
@ -207,9 +211,10 @@ inline detail::type_info *get_global_type_info(const std::type_index &tp) {
return nullptr; 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, 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)) { if (auto *ltype = get_local_type_info(tp)) {
return ltype; return ltype;
} }
@ -220,7 +225,8 @@ PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_index &tp,
if (throw_if_missing) { if (throw_if_missing) {
std::string tname = tp.name(); std::string tname = tp.name();
detail::clean_type_id(tname); 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; return nullptr;
} }
@ -232,7 +238,7 @@ PYBIND11_NOINLINE handle get_type_handle(const std::type_info &tp, bool throw_if
// Searches the inheritance graph for a registered Python instance, using all_type_info(). // Searches the inheritance graph for a registered Python instance, using all_type_info().
PYBIND11_NOINLINE handle find_registered_python_instance(void *src, 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); 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 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))) { for (auto *instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
@ -251,10 +257,10 @@ struct value_and_holder {
void **vh = nullptr; void **vh = nullptr;
// Main constructor for a found value/holder: // Main constructor for a found value/holder:
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index)
inst{i}, index{index}, type{type}, : inst{i}, index{index}, type{type}, vh{inst->simple_layout
vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} ? 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()) // Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
value_and_holder() = default; value_and_holder() = default;
@ -262,13 +268,15 @@ struct value_and_holder {
// Used for past-the-end iterator // Used for past-the-end iterator
explicit value_and_holder(size_t index) : index{index} {} 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]); return reinterpret_cast<V *&>(vh[0]);
} }
// True if this `value_and_holder` has a non-null value pointer // True if this `value_and_holder` has a non-null value pointer
explicit operator bool() const { return value_ptr() != nullptr; } 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]); return reinterpret_cast<H &>(vh[1]);
} }
bool holder_constructed() const { bool holder_constructed() const {
@ -288,8 +296,8 @@ struct value_and_holder {
} }
bool instance_registered() const { bool instance_registered() const {
return inst->simple_layout return inst->simple_layout
? inst->simple_instance_registered ? inst->simple_instance_registered
: ((inst->nonsimple.status[index] & instance::status_instance_registered) != 0); : ((inst->nonsimple.status[index] & instance::status_instance_registered) != 0);
} }
// NOLINTNEXTLINE(readability-make-member-function-const) // NOLINTNEXTLINE(readability-make-member-function-const)
void set_instance_registered(bool v = true) { void set_instance_registered(bool v = true) {
@ -322,11 +330,10 @@ public:
friend struct values_and_holders; friend struct values_and_holders;
iterator(instance *inst, const type_vec *tinfo) iterator(instance *inst, const type_vec *tinfo)
: inst{inst}, types{tinfo}, : inst{inst}, types{tinfo},
curr(inst /* instance */, curr(inst /* instance */,
types->empty() ? nullptr : (*types)[0] /* type info */, types->empty() ? nullptr : (*types)[0] /* type info */,
0, /* vpos: (non-simple types only): the first vptr comes first */ 0, /* vpos: (non-simple types only): the first vptr comes first */
0 /* index */) 0 /* index */) {}
{}
// Past-the-end iterator: // Past-the-end iterator:
explicit iterator(size_t end) : curr(end) {} explicit iterator(size_t end) : curr(end) {}
@ -369,7 +376,9 @@ public:
* The returned object should be short-lived: in particular, it must not outlive the called-upon * The returned object should be short-lived: in particular, it must not outlive the called-upon
* instance. * 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: // Optimize common case:
if (!find_type || Py_TYPE(this) == find_type->type) { if (!find_type || Py_TYPE(this) == find_type->type) {
return value_and_holder(this, find_type, 0, 0); return value_and_holder(this, find_type, 0, 0);
@ -387,12 +396,13 @@ PYBIND11_NOINLINE value_and_holder instance::get_value_and_holder(const type_inf
#if defined(NDEBUG) #if defined(NDEBUG)
pybind11_fail("pybind11::detail::instance::get_value_and_holder: " pybind11_fail("pybind11::detail::instance::get_value_and_holder: "
"type is not a pybind11 base of the given instance " "type is not a pybind11 base of the given instance "
"(compile in debug mode for type details)"); "(compile in debug mode for type details)");
#else #else
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + 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(find_type->type)
get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance"); + "' is not a pybind11 base of the given `"
+ get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance");
#endif #endif
} }
@ -406,23 +416,22 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
"instance allocation failed: new instance has no pybind11-registered base types"); "instance allocation failed: new instance has no pybind11-registered base types");
} }
simple_layout = simple_layout
n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); = 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 // Simple path: no python-side multiple inheritance, and a small-enough holder
if (simple_layout) { if (simple_layout) {
simple_value_holder[0] = nullptr; simple_value_holder[0] = nullptr;
simple_holder_constructed = false; simple_holder_constructed = false;
simple_instance_registered = 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, // 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 // [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 // values that tracks whether each associated holder has been initialized. Each [block] is
// padded, if necessary, to an integer multiple of sizeof(void *). // padded, if necessary, to an integer multiple of sizeof(void *).
size_t space = 0; size_t space = 0;
for (auto *t : tinfo) { for (auto *t : tinfo) {
space += 1; // value pointer space += 1; // value pointer
space += t->holder_size_in_ptrs; // holder instance space += t->holder_size_in_ptrs; // holder instance
} }
size_t flags_at = space; size_t flags_at = space;
@ -441,10 +450,12 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
} }
#else #else
nonsimple.values_and_holders = (void **) PyMem_New(void *, space); 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 *)); std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *));
#endif #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; owned = true;
} }
@ -501,17 +512,16 @@ PYBIND11_NOINLINE std::string error_string() {
PyFrameObject *frame = trace->tb_frame; PyFrameObject *frame = trace->tb_frame;
errorString += "\n\nAt:\n"; errorString += "\n\nAt:\n";
while (frame) { while (frame) {
#if PY_VERSION_HEX >= 0x03090000 # if PY_VERSION_HEX >= 0x03090000
PyCodeObject *f_code = PyFrame_GetCode(frame); PyCodeObject *f_code = PyFrame_GetCode(frame);
#else # else
PyCodeObject *f_code = frame->f_code; PyCodeObject *f_code = frame->f_code;
Py_INCREF(f_code); Py_INCREF(f_code);
#endif # endif
int lineno = PyFrame_GetLineNumber(frame); int lineno = PyFrame_GetLineNumber(frame);
errorString += errorString += " " + handle(f_code->co_filename).cast<std::string>() + "("
" " + handle(f_code->co_filename).cast<std::string>() + + std::to_string(lineno)
"(" + std::to_string(lineno) + "): " + + "): " + handle(f_code->co_name).cast<std::string>() + "\n";
handle(f_code->co_name).cast<std::string>() + "\n";
frame = frame->f_back; frame = frame->f_back;
Py_DECREF(f_code); Py_DECREF(f_code);
} }
@ -521,7 +531,7 @@ PYBIND11_NOINLINE std::string error_string() {
return errorString; 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 &instances = get_internals().registered_instances;
auto range = instances.equal_range(ptr); auto range = instances.equal_range(ptr);
for (auto it = range.first; it != range.second; ++it) { for (auto it = range.first; it != range.second; ++it) {
@ -540,9 +550,9 @@ inline PyThreadState *get_thread_state_unchecked() {
#elif PY_VERSION_HEX < 0x03000000 #elif PY_VERSION_HEX < 0x03000000
return _PyThreadState_Current; return _PyThreadState_Current;
#elif PY_VERSION_HEX < 0x03050000 #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 #elif PY_VERSION_HEX < 0x03050200
return (PyThreadState*) _PyThreadState_Current.value; return (PyThreadState *) _PyThreadState_Current.value;
#else #else
return _PyThreadState_UncheckedGet(); return _PyThreadState_UncheckedGet();
#endif #endif
@ -560,11 +570,11 @@ public:
explicit type_caster_generic(const type_info *typeinfo) explicit type_caster_generic(const type_info *typeinfo)
: typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) {} : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) {}
bool load(handle src, bool convert) { bool load(handle src, bool convert) { return load_impl<type_caster_generic>(src, 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, const detail::type_info *tinfo,
void *(*copy_constructor)(const void *), void *(*copy_constructor)(const void *),
void *(*move_constructor)(const void *), void *(*move_constructor)(const void *),
@ -610,8 +620,8 @@ public:
#else #else
std::string type_name(tinfo->cpptype->name()); std::string type_name(tinfo->cpptype->name());
detail::clean_type_id(type_name); detail::clean_type_id(type_name);
throw cast_error("return_value_policy = copy, but type " + throw cast_error("return_value_policy = copy, but type " + type_name
type_name + " is non-copyable!"); + " is non-copyable!");
#endif #endif
} }
wrapper->owned = true; wrapper->owned = true;
@ -630,8 +640,8 @@ public:
#else #else
std::string type_name(tinfo->cpptype->name()); std::string type_name(tinfo->cpptype->name());
detail::clean_type_id(type_name); detail::clean_type_id(type_name);
throw cast_error("return_value_policy = move, but type " + throw cast_error("return_value_policy = move, but type " + type_name
type_name + " is neither movable nor copyable!"); + " is neither movable nor copyable!");
#endif #endif
} }
wrapper->owned = true; wrapper->owned = true;
@ -661,16 +671,15 @@ public:
if (type->operator_new) { if (type->operator_new) {
vptr = type->operator_new(type->type_size); vptr = type->operator_new(type->type_size);
} else { } else {
#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
vptr = ::operator new(type->type_size, vptr = ::operator new(type->type_size, std::align_val_t(type->type_align));
std::align_val_t(type->type_align)); } else {
} else {
vptr = ::operator new(type->type_size);
}
#else
vptr = ::operator new(type->type_size); vptr = ::operator new(type->type_size);
#endif }
#else
vptr = ::operator new(type->type_size);
#endif
} }
} }
value = vptr; value = vptr;
@ -713,7 +722,8 @@ public:
} }
type_info *foreign_typeinfo = reinterpret_borrow<capsule>(getattr(pytype, local_key)); 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 if (foreign_typeinfo->module_local_load == &local_load
|| (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) { || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) {
return false; return false;
@ -764,21 +774,23 @@ public:
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder()); this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
return true; return true;
} }
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see
// we can find an exact match (or, for a simple C++ type, an inherited match); if so, we // if we can find an exact match (or, for a simple C++ type, an inherited match); if
// can safely reinterpret_cast to the relevant pointer. // so, we can safely reinterpret_cast to the relevant pointer.
if (bases.size() > 1) { if (bases.size() > 1) {
for (auto *base : bases) { for (auto *base : bases) {
if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type)
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base)); : base->type == typeinfo->type) {
this_.load_value(
reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base));
return true; return true;
} }
} }
} }
// Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type
// in the registered bases, above, so try implicit casting (needed for proper C++ casting // match in the registered bases, above, so try implicit casting (needed for proper C++
// when MI is involved). // casting when MI is involved).
if (this_.try_implicit_casts(src, convert)) { if (this_.try_implicit_casts(src, convert)) {
return true; return true;
} }
@ -808,28 +820,29 @@ public:
// Global typeinfo has precedence over foreign module_local // Global typeinfo has precedence over foreign module_local
if (try_load_foreign_module_local(src)) { if (try_load_foreign_module_local(src)) {
return true; return true;
} }
// Custom converters didn't take None, now we convert None to nullptr. // Custom converters didn't take None, now we convert None to nullptr.
if (src.is_none()) { if (src.is_none()) {
// Defer accepting None to other overloads (if we aren't in convert mode): // Defer accepting None to other overloads (if we aren't in convert mode):
if (!convert) { if (!convert) {
return false; return false;
} }
value = nullptr; value = nullptr;
return true; return true;
} }
return false; return false;
} }
// Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast // 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 // 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). // 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( PYBIND11_NOINLINE static std::pair<const void *, const type_info *>
const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { 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)) { if (auto *tpi = get_type_info(cast_type)) {
return {src, const_cast<const type_info *>(tpi)}; return {src, const_cast<const type_info *>(tpi)};
} }
@ -855,10 +868,9 @@ public:
* `movable_cast_op_type` instead. * `movable_cast_op_type` instead.
*/ */
template <typename T> template <typename T>
using cast_op_type = using cast_op_type = conditional_t<std::is_pointer<remove_reference_t<T>>::value,
conditional_t<std::is_pointer<remove_reference_t<T>>::value, typename std::add_pointer<intrinsic_t<T>>::type,
typename std::add_pointer<intrinsic_t<T>>::type, typename std::add_lvalue_reference<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 * Determine suitable casting operator for a type caster with a movable value. Such a type caster
@ -868,40 +880,50 @@ using cast_op_type =
* These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro.
*/ */
template <typename T> template <typename T>
using movable_cast_op_type = using movable_cast_op_type
conditional_t<std::is_pointer<typename std::remove_reference<T>::type>::value, = conditional_t<std::is_pointer<typename std::remove_reference<T>::type>::value,
typename std::add_pointer<intrinsic_t<T>>::type, typename std::add_pointer<intrinsic_t<T>>::type,
conditional_t<std::is_rvalue_reference<T>::value, conditional_t<std::is_rvalue_reference<T>::value,
typename std::add_rvalue_reference<intrinsic_t<T>>::type, typename std::add_rvalue_reference<intrinsic_t<T>>::type,
typename std::add_lvalue_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 // 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. // 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 // 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 // (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. // 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< template <typename Container>
std::is_copy_constructible<Container>, struct is_copy_constructible<
std::is_same<typename Container::value_type &, typename Container::reference>, Container,
// Avoid infinite recursion enable_if_t<
negation<std::is_same<Container, typename Container::value_type>> all_of<std::is_copy_constructible<Container>,
>::value>> : is_copy_constructible<typename Container::value_type> {}; 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 // Likewise for std::pair
// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves // (after C++17 it is mandatory that the copy constructor not exist when the two types aren't
// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers). // themselves copy constructible, but this can not be relied upon when T1 or T2 are themselves
template <typename T1, typename T2> struct is_copy_constructible<std::pair<T1, T2>> // containers).
template <typename T1, typename T2>
struct is_copy_constructible<std::pair<T1, T2>>
: all_of<is_copy_constructible<T1>, is_copy_constructible<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. // 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 T, typename SFINAE = void>
template <typename Container> struct is_copy_assignable<Container, enable_if_t<all_of< struct is_copy_assignable : std::is_copy_assignable<T> {};
std::is_copy_assignable<Container>, template <typename Container>
std::is_same<typename Container::value_type &, typename Container::reference> struct is_copy_assignable<Container,
>::value>> : is_copy_assignable<typename Container::value_type> {}; enable_if_t<all_of<std::is_copy_assignable<Container>,
template <typename T1, typename T2> struct is_copy_assignable<std::pair<T1, T2>> 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>> {}; : all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
@ -928,16 +950,14 @@ PYBIND11_NAMESPACE_END(detail)
// std::enable_if. User provided specializations will always have higher priority than // std::enable_if. User provided specializations will always have higher priority than
// the default implementation and specialization provided in polymorphic_type_hook_base. // the default implementation and specialization provided in polymorphic_type_hook_base.
template <typename itype, typename SFINAE = void> template <typename itype, typename SFINAE = void>
struct polymorphic_type_hook_base struct polymorphic_type_hook_base {
{ static const void *get(const itype *src, const std::type_info *&) { return src; }
static const void *get(const itype *src, const std::type_info*&) { return src; }
}; };
template <typename itype> template <typename itype>
struct polymorphic_type_hook_base<itype, detail::enable_if_t<std::is_polymorphic<itype>::value>> 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) {
static const void *get(const itype *src, const std::type_info*& type) {
type = src ? &typeid(*src) : nullptr; type = src ? &typeid(*src) : nullptr;
return dynamic_cast<const void*>(src); return dynamic_cast<const void *>(src);
} }
}; };
template <typename itype, typename SFINAE = void> template <typename itype, typename SFINAE = void>
@ -946,14 +966,15 @@ struct polymorphic_type_hook : public polymorphic_type_hook_base<itype> {};
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
/// Generic type caster for objects stored on the heap /// 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>; using itype = intrinsic_t<type>;
public: public:
static constexpr auto name = const_name<type>(); static constexpr auto name = const_name<type>();
type_caster_base() : type_caster_base(typeid(type)) { } type_caster_base() : type_caster_base(typeid(type)) {}
explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } 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) { static handle cast(const itype &src, return_value_policy policy, handle parent) {
if (policy == return_value_policy::automatic if (policy == return_value_policy::automatic
@ -987,31 +1008,39 @@ public:
return {vsrc, tpi}; return {vsrc, tpi};
} }
} }
// Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer,
// don't do a cast // so don't do a cast
return type_caster_generic::src_and_type(src, cast_type, instance_type); return type_caster_generic::src_and_type(src, cast_type, instance_type);
} }
static handle cast(const itype *src, return_value_policy policy, handle parent) { static handle cast(const itype *src, return_value_policy policy, handle parent) {
auto st = src_and_type(src); auto st = src_and_type(src);
return type_caster_generic::cast( return type_caster_generic::cast(st.first,
st.first, policy, parent, st.second, policy,
make_copy_constructor(src), make_move_constructor(src)); parent,
st.second,
make_copy_constructor(src),
make_move_constructor(src));
} }
static handle cast_holder(const itype *src, const void *holder) { static handle cast_holder(const itype *src, const void *holder) {
auto st = src_and_type(src); auto st = src_and_type(src);
return type_caster_generic::cast( return type_caster_generic::cast(st.first,
st.first, return_value_policy::take_ownership, {}, st.second, return_value_policy::take_ownership,
nullptr, nullptr, holder); {},
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) // NOLINTNEXTLINE(google-explicit-constructor)
operator itype*() { return (type *) value; } operator itype *() { return (type *) value; }
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
operator itype&() { operator itype &() {
if (!value) { if (!value) {
throw reference_cast_error(); throw reference_cast_error();
} }
@ -1019,20 +1048,20 @@ public:
} }
protected: protected:
using Constructor = void *(*)(const void *); using Constructor = void *(*) (const void *);
/* Only enabled when the types are {copy,move}-constructible *and* when the type /* 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 does not have a private operator new implementation. A comma operator is used in the
argument to apply SFINAE to the public copy/move constructors.*/ decltype argument to apply SFINAE to the public copy/move constructors.*/
template <typename T, typename = enable_if_t<is_copy_constructible<T>::value>> 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{}) { static auto make_copy_constructor(const T *)
return [](const void *arg) -> void * { -> decltype(new T(std::declval<const T>()), Constructor{}) {
return new T(*reinterpret_cast<const T *>(arg)); 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>> 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 [](const void *arg) -> void * {
return new T(std::move(*const_cast<T *>(reinterpret_cast<const T *>(arg)))); return new T(std::move(*const_cast<T *>(reinterpret_cast<const T *>(arg))));
}; };

9
include/pybind11/detail/typeid.h

@ -13,7 +13,7 @@
#include <cstdlib> #include <cstdlib>
#if defined(__GNUG__) #if defined(__GNUG__)
#include <cxxabi.h> # include <cxxabi.h>
#endif #endif
#include "common.h" #include "common.h"
@ -34,8 +34,8 @@ inline void erase_all(std::string &string, const std::string &search) {
PYBIND11_NOINLINE void clean_type_id(std::string &name) { PYBIND11_NOINLINE void clean_type_id(std::string &name) {
#if defined(__GNUG__) #if defined(__GNUG__)
int status = 0; int status = 0;
std::unique_ptr<char, void (*)(void *)> res { std::unique_ptr<char, void (*)(void *)> res{
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free};
if (status == 0) { if (status == 0) {
name = res.get(); name = res.get();
} }
@ -49,7 +49,8 @@ PYBIND11_NOINLINE void clean_type_id(std::string &name) {
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/// Return a string representation of a C++ type /// 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()); std::string name(typeid(T).name());
detail::clean_type_id(name); detail::clean_type_id(name);
return name; return name;

395
include/pybind11/eigen.h

@ -23,122 +23,145 @@
// 2. we definitely want to ignore any MSVC warnings originating from Eigen code, // 2. we definitely want to ignore any MSVC warnings originating from Eigen code,
// it is probably best to keep this around indefinitely. // it is probably best to keep this around indefinitely.
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 4127) // C4127: conditional expression is constant # pragma warning(disable : 4127) // C4127: conditional expression is constant
#endif #endif
#include <Eigen/Core> #include <Eigen/Core>
#include <Eigen/SparseCore> #include <Eigen/SparseCore>
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(pop) # pragma warning(pop)
#endif #endif
// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit // 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 // move constructors that break things. We could detect this an explicitly copy, but an extra copy
// of matrices seems highly undesirable. // 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) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: // Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>; using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
template <typename MatrixType> using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>; template <typename MatrixType>
template <typename MatrixType> using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>; using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
template <typename MatrixType>
using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if EIGEN_VERSION_AT_LEAST(3,3,0) #if EIGEN_VERSION_AT_LEAST(3, 3, 0)
using EigenIndex = Eigen::Index; 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>>; using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
#else #else
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; 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>; using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
#endif #endif
// Matches Eigen::Map, Eigen::Ref, blocks, etc: // 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>
template <typename T> using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>; using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>,
template <typename T> using is_eigen_dense_plain = all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>; std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
template <typename T> using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, 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 // 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 // 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 // matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
// SelfAdjointView fall into this category. // SelfAdjointView fall into this category.
template <typename T> using is_eigen_other = all_of< template <typename T>
is_template_base_of<Eigen::EigenBase, T>, using is_eigen_other
negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>> = 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()): // Captures numpy/eigen conformability status (returned by EigenProps::conformable()):
template <bool EigenRowMajor> struct EigenConformable { template <bool EigenRowMajor>
struct EigenConformable {
bool conformable = false; bool conformable = false;
EigenIndex rows = 0, cols = 0; EigenIndex rows = 0, cols = 0;
EigenDStride stride{0, 0}; // Only valid if negativestrides is false! EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
bool negativestrides = false; // If true, do not use stride! bool negativestrides = false; // If true, do not use stride!
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
EigenConformable(bool fits = false) : conformable{fits} {} EigenConformable(bool fits = false) : conformable{fits} {}
// Matrix type: // Matrix type:
EigenConformable(EigenIndex r, EigenIndex c, EigenConformable(EigenIndex r, EigenIndex c, EigenIndex rstride, EigenIndex cstride)
EigenIndex rstride, EigenIndex cstride) : : conformable{true}, rows{r}, cols{c},
conformable{true}, rows{r}, cols{c}, // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
//TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 // http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
stride{EigenRowMajor ? (rstride > 0 ? rstride : 0) : (cstride > 0 ? cstride : 0) /* outer stride */, stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
EigenRowMajor ? (cstride > 0 ? cstride : 0) : (rstride > 0 ? rstride : 0) /* inner stride */ }, : (cstride > 0 ? cstride : 0) /* outer stride */,
negativestrides{rstride < 0 || cstride < 0} { EigenRowMajor ? (cstride > 0 ? cstride : 0)
: (rstride > 0 ? rstride : 0) /* inner stride */},
} negativestrides{rstride < 0 || cstride < 0} {}
// Vector type: // Vector type:
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) 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, // 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) // matching strides, or a dimension size of 1 (in which case the stride value is
return // irrelevant)
!negativestrides && return !negativestrides
(props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || && (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
(EigenRowMajor ? cols : rows) == 1) && || (EigenRowMajor ? cols : rows) == 1)
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || && (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
(EigenRowMajor ? rows : cols) == 1); || (EigenRowMajor ? rows : cols) == 1);
} }
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
operator bool() const { return conformable; } 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> 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> 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 // Helper struct for extracting information from an Eigen type
template <typename Type_> struct EigenProps { template <typename Type_>
struct EigenProps {
using Type = Type_; using Type = Type_;
using Scalar = typename Type::Scalar; using Scalar = typename Type::Scalar;
using StrideType = typename eigen_extract_stride<Type>::type; using StrideType = typename eigen_extract_stride<Type>::type;
static constexpr EigenIndex static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
rows = Type::RowsAtCompileTime, size = Type::SizeAtCompileTime;
cols = Type::ColsAtCompileTime, static constexpr bool row_major = Type::IsRowMajor,
size = Type::SizeAtCompileTime; vector
static constexpr bool = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
row_major = Type::IsRowMajor, fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic,
vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 fixed = size != Eigen::Dynamic, // Fully-fixed size
fixed_rows = rows != Eigen::Dynamic, dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
fixed_cols = cols != Eigen::Dynamic,
fixed = size != Eigen::Dynamic, // Fully-fixed size template <EigenIndex i, EigenIndex ifzero>
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
static constexpr EigenIndex inner_stride
template <EigenIndex i, EigenIndex ifzero> using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>; = if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
static constexpr EigenIndex inner_stride = if_zero<StrideType::InnerStrideAtCompileTime, 1>::value, outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
outer_stride = if_zero<StrideType::OuterStrideAtCompileTime, vector ? size
vector ? size : row_major ? cols : rows>::value; : row_major ? cols
static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; : rows > ::value;
static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; static constexpr bool dynamic_stride
static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; = 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 // 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 // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector
@ -151,11 +174,9 @@ template <typename Type_> struct EigenProps {
if (dims == 2) { // Matrix type: require exact match (or dynamic) if (dims == 2) { // Matrix type: require exact match (or dynamic)
EigenIndex EigenIndex np_rows = a.shape(0), np_cols = a.shape(1),
np_rows = a.shape(0), np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
np_cols = a.shape(1), np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
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) if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows)
|| (PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols)) { || (PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols)) {
return false; return false;
@ -164,10 +185,10 @@ template <typename Type_> struct EigenProps {
return {np_rows, np_cols, np_rstride, np_cstride}; 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 // Otherwise we're storing an n-vector. Only one of the strides will be used, but
// is used, we want the (single) numpy stride value. // whichever is used, we want the (single) numpy stride value.
const EigenIndex n = a.shape(0), 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 (vector) { // Eigen type is a compile-time vector
if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n) { if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n) {
@ -190,38 +211,41 @@ template <typename Type_> struct EigenProps {
if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) { if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) {
return false; 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_order = is_eigen_dense_map<Type>::value;
static constexpr bool show_c_contiguous = show_order && requires_row_major; static constexpr bool show_c_contiguous = show_order && requires_row_major;
static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; static constexpr bool show_f_contiguous
= !show_c_contiguous && show_order && requires_col_major;
static constexpr auto descriptor =
const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + static constexpr auto descriptor
const_name("[") + const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + = const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
const_name(", ") + const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + + const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
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 // For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
// options, possibly f_contiguous or c_contiguous. We include them in the descriptor output // be satisfied: writeable=True (for a mutable reference), and, depending on the map's
// to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to // stride options, possibly f_contiguous or c_contiguous. We include them in the
// see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you // descriptor output to provide some hint as to why a TypeError is occurring (otherwise
// *gave* a numpy.ndarray of the right type and dimensions. // it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and
const_name<show_writeable>(", flags.writeable", "") + // an error message that you *gave* a numpy.ndarray of the right type and dimensions.
const_name<show_c_contiguous>(", flags.c_contiguous", "") + const_name<show_writeable>(", flags.writeable", "")
const_name<show_f_contiguous>(", flags.f_contiguous", "") + + const_name<show_c_contiguous>(", flags.c_contiguous", "")
const_name("]"); + 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, // 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. // 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); constexpr ssize_t elem_size = sizeof(typename props::Scalar);
array a; array a;
if (props::vector) { 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 { } else {
a = array({src.rows(), src.cols()}, a = array({src.rows(), src.cols()},
{elem_size * src.rowStride(), elem_size * src.colStride()}, {elem_size * src.rowStride(), elem_size * src.colStride()},
@ -247,10 +271,10 @@ handle eigen_ref_array(Type &src, handle parent = none()) {
return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value); 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 // Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
// array that references the encapsulated data with a python-side reference to the capsule to tie // numpy array that references the encapsulated data with a python-side reference to the capsule to
// its destruction to that of any dependent python objects. Const-ness is determined by whether or // tie its destruction to that of any dependent python objects. Const-ness is determined by
// not the Type of the pointer given is const. // 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>> template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
handle eigen_encapsulate(Type *src) { handle eigen_encapsulate(Type *src) {
capsule base(src, [](void *o) { delete static_cast<Type *>(o); }); capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
@ -259,7 +283,7 @@ handle eigen_encapsulate(Type *src) {
// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense // Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense
// types. // types.
template<typename Type> template <typename Type>
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> { struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
using Scalar = typename Type::Scalar; using Scalar = typename Type::Scalar;
using props = EigenProps<Type>; using props = EigenProps<Type>;
@ -307,7 +331,6 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
} }
private: private:
// Cast implementation // Cast implementation
template <typename CType> template <typename CType>
static handle cast_impl(CType *src, return_value_policy policy, handle parent) { static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
@ -330,7 +353,6 @@ private:
} }
public: public:
// Normal returned non-reference, non-const value: // Normal returned non-reference, non-const value:
static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { static handle cast(Type &&src, return_value_policy /* policy */, handle parent) {
return cast_impl(&src, return_value_policy::move, parent); return cast_impl(&src, return_value_policy::move, parent);
@ -367,30 +389,31 @@ public:
static constexpr auto name = props::descriptor; static constexpr auto name = props::descriptor;
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
operator Type*() { return &value; } operator Type *() { return &value; }
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return value; } operator Type &() { return value; }
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
operator Type&&() && { return std::move(value); } operator Type &&() && { return std::move(value); }
template <typename T> using cast_op_type = movable_cast_op_type<T>; template <typename T>
using cast_op_type = movable_cast_op_type<T>;
private: private:
Type value; Type value;
}; };
// Base class for casting reference/map/block/etc. objects back to python. // 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: private:
using props = EigenProps<MapType>; using props = EigenProps<MapType>;
public: public:
// Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has // 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 // to stay around), but we'll allow it under the assumption that you know what you're doing
// have an appropriate keep_alive in place). We return a numpy array pointing directly at the // (and have an appropriate keep_alive in place). We return a numpy array pointing directly at
// ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note // the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
// that this means you need to ensure you don't destroy the object in some other way (e.g. with // Note that this means you need to ensure you don't destroy the object in some other way (e.g.
// an appropriate keep_alive, or with a reference to a statically allocated matrix). // 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) { static handle cast(const MapType &src, return_value_policy policy, handle parent) {
switch (policy) { switch (policy) {
case return_value_policy::copy: case return_value_policy::copy:
@ -414,43 +437,50 @@ public:
// you end up here if you try anyway. // you end up here if you try anyway.
bool load(handle, bool) = delete; bool load(handle, bool) = delete;
operator MapType() = 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): // 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>> template <typename Type>
: eigen_map_caster<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 // 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). // copying (it requires some extra effort in many cases).
template <typename PlainObjectType, typename StrideType> template <typename PlainObjectType, typename StrideType>
struct type_caster< struct type_caster<
Eigen::Ref<PlainObjectType, 0, StrideType>, Eigen::Ref<PlainObjectType, 0, StrideType>,
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value> enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
> : public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> { : public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
private: private:
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>; using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
using props = EigenProps<Type>; using props = EigenProps<Type>;
using Scalar = typename props::Scalar; using Scalar = typename props::Scalar;
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>; using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
using Array = array_t<Scalar, array::forcecast | using Array
((props::row_major ? props::inner_stride : props::outer_stride) == 1 ? array::c_style : = array_t<Scalar,
(props::row_major ? props::outer_stride : props::inner_stride) == 1 ? array::f_style : 0)>; 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; static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value;
// Delay construction (these have no default constructor) // Delay construction (these have no default constructor)
std::unique_ptr<MapType> map; std::unique_ptr<MapType> map;
std::unique_ptr<Type> ref; std::unique_ptr<Type> ref;
// Our array. When possible, this is just a numpy array pointing to the source data, but // 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 // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
// layout, or is an array of a type that needs to be converted). Using a numpy temporary // incompatible layout, or is an array of a type that needs to be converted). Using a numpy
// (rather than an Eigen temporary) saves an extra copy when we need both type conversion and // temporary (rather than an Eigen temporary) saves an extra copy when we need both type
// storage order conversion. (Note that we refuse to use this temporary copy when loading an // conversion and storage order conversion. (Note that we refuse to use this temporary copy
// argument for a Ref<M> with M non-const, i.e. a read-write reference). // when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
Array copy_or_ref; Array copy_or_ref;
public: public:
bool load(handle src, bool convert) { 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 // First check whether what we have is already an array of the right type. If not, we
// avoid a copy (because the copy is also going to do type conversion). // can't avoid a copy (because the copy is also going to do type conversion).
bool need_copy = !isinstance<Array>(src); bool need_copy = !isinstance<Array>(src);
EigenConformable<props::row_major> fits; EigenConformable<props::row_major> fits;
@ -469,8 +499,7 @@ public:
} else { } else {
copy_or_ref = std::move(aref); copy_or_ref = std::move(aref);
} }
} } else {
else {
need_copy = true; need_copy = true;
} }
} }
@ -496,54 +525,76 @@ public:
} }
ref.reset(); 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)); ref.reset(new Type(*map));
return true; return true;
} }
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
operator Type*() { return ref.get(); } operator Type *() { return ref.get(); }
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return *ref; } operator Type &() { return *ref; }
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>;
private: private:
template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0> 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> 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. // Attempt to figure out a constructor of `Stride` that will work.
// If both strides are fixed, use a default constructor: // If both strides are fixed, use a default constructor:
template <typename S> using stride_ctor_default = bool_constant< template <typename S>
S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
std::is_default_constructible<S>::value>; && S::OuterStrideAtCompileTime != Eigen::Dynamic
&& std::is_default_constructible<S>::value>;
// Otherwise, if there is a two-index constructor, assume it is (outer,inner) like // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
// Eigen::Stride, and use it: // Eigen::Stride, and use it:
template <typename S> using stride_ctor_dual = bool_constant< template <typename S>
!stride_ctor_default<S>::value && std::is_constructible<S, EigenIndex, EigenIndex>::value>; 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 // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use
// it (passing whichever stride is dynamic). // it (passing whichever stride is dynamic).
template <typename S> using stride_ctor_outer = bool_constant< template <typename S>
!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value && using stride_ctor_outer
S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && = bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
std::is_constructible<S, EigenIndex>::value>; && S::OuterStrideAtCompileTime == Eigen::Dynamic
template <typename S> using stride_ctor_inner = bool_constant< && S::InnerStrideAtCompileTime != Eigen::Dynamic
!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value && && std::is_constructible<S, EigenIndex>::value>;
S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && template <typename S>
std::is_constructible<S, EigenIndex>::value>; 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> 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> 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> 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> 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 // type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not
@ -553,14 +604,18 @@ private:
template <typename Type> template <typename Type>
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> { struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
protected: 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>; using props = EigenProps<Matrix>;
public: public:
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
handle h = eigen_encapsulate<props>(new Matrix(src)); handle h = eigen_encapsulate<props>(new Matrix(src));
return h; 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; static constexpr auto name = props::descriptor;
@ -569,10 +624,11 @@ public:
// you end up here if you try anyway. // you end up here if you try anyway.
bool load(handle, bool) = delete; bool load(handle, bool) = delete;
operator Type() = 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>> { struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
using Scalar = typename Type::Scalar; using Scalar = typename Type::Scalar;
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>; using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
@ -586,8 +642,7 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
auto obj = reinterpret_borrow<object>(src); auto obj = reinterpret_borrow<object>(src);
object sparse_module = module_::import("scipy.sparse"); object sparse_module = module_::import("scipy.sparse");
object matrix_type = sparse_module.attr( object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
rowMajor ? "csr_matrix" : "csc_matrix");
if (!type::handle_of(obj).is(matrix_type)) { if (!type::handle_of(obj).is(matrix_type)) {
try { try {
@ -608,32 +663,36 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
} }
value = EigenMapSparseMatrix<Scalar, value = EigenMapSparseMatrix<Scalar,
Type::Flags & (Eigen::RowMajor | Eigen::ColMajor), Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
StorageIndex>( StorageIndex>(shape[0].cast<Index>(),
shape[0].cast<Index>(), shape[1].cast<Index>(), nnz, shape[1].cast<Index>(),
outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); nnz,
outerIndices.mutable_data(),
innerIndices.mutable_data(),
values.mutable_data());
return true; return true;
} }
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { 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( object matrix_type
rowMajor ? "csr_matrix" : "csc_matrix"); = module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
array data(src.nonZeros(), src.valuePtr()); array data(src.nonZeros(), src.valuePtr());
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
array innerIndices(src.nonZeros(), src.innerIndexPtr()); array innerIndices(src.nonZeros(), src.innerIndexPtr());
return matrix_type( return matrix_type(std::make_tuple(data, innerIndices, outerIndices),
std::make_tuple(data, innerIndices, outerIndices), std::make_pair(src.rows(), src.cols()))
std::make_pair(src.rows(), src.cols()) .release();
).release();
} }
PYBIND11_TYPE_CASTER(Type, const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") PYBIND11_TYPE_CASTER(Type,
+ npy_format_descriptor<Scalar>::name + const_name("]")); const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
"scipy.sparse.csc_matrix[")
+ npy_format_descriptor<Scalar>::name + const_name("]"));
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

34
include/pybind11/embed.h

@ -16,21 +16,17 @@
#include <vector> #include <vector>
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
# error Embedding the interpreter is not supported with PyPy # error Embedding the interpreter is not supported with PyPy
#endif #endif
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ # define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
extern "C" PyObject *pybind11_init_impl_##name(); \ extern "C" PyObject *pybind11_init_impl_##name(); \
extern "C" PyObject *pybind11_init_impl_##name() { \ extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); }
return pybind11_init_wrapper_##name(); \
}
#else #else
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ # define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
extern "C" void pybind11_init_impl_##name(); \ extern "C" void pybind11_init_impl_##name(); \
extern "C" void pybind11_init_impl_##name() { \ extern "C" void pybind11_init_impl_##name() { pybind11_init_wrapper_##name(); }
pybind11_init_wrapper_##name(); \
}
#endif #endif
/** \rst /** \rst
@ -72,7 +68,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. /// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
struct embedded_module { struct embedded_module {
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
using init_t = PyObject *(*)(); using init_t = PyObject *(*) ();
#else #else
using init_t = void (*)(); using init_t = void (*)();
#endif #endif
@ -106,10 +102,10 @@ inline wchar_t *widen_chars(const char *safe_arg) {
wchar_t *widened_arg = nullptr; wchar_t *widened_arg = nullptr;
// warning C4996: 'mbstowcs': This function or variable may be unsafe. // warning C4996: 'mbstowcs': This function or variable may be unsafe.
#if defined(_MSC_VER) # if defined(_MSC_VER)
#pragma warning(push) # pragma warning(push)
#pragma warning(disable:4996) # pragma warning(disable : 4996)
#endif # endif
# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS # if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS
size_t count = std::strlen(safe_arg); size_t count = std::strlen(safe_arg);
@ -121,9 +117,9 @@ inline wchar_t *widen_chars(const char *safe_arg) {
std::mbstowcs(widened_arg, safe_arg, count + 1); std::mbstowcs(widened_arg, safe_arg, count + 1);
} }
#if defined(_MSC_VER) # if defined(_MSC_VER)
#pragma warning(pop) # pragma warning(pop)
#endif # endif
#endif #endif
return widened_arg; return widened_arg;

87
include/pybind11/eval.h

@ -11,24 +11,24 @@
#pragma once #pragma once
#include <utility>
#include "pybind11.h" #include "pybind11.h"
#include <utility>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
inline void ensure_builtins_in_globals(object &global) { inline void ensure_builtins_in_globals(object &global) {
#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000 #if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000
// Running exec and eval on Python 2 and 3 adds `builtins` module under // Running exec and eval on Python 2 and 3 adds `builtins` module under
// `__builtins__` key to globals if not yet present. // `__builtins__` key to globals if not yet present.
// Python 3.8 made PyRun_String behave similarly. Let's also do that for // 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. // older versions, for consistency. This was missing from PyPy3.8 7.3.7.
if (!global.contains("__builtins__")) if (!global.contains("__builtins__"))
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE); global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
#else #else
(void) global; (void) global;
#endif #endif
} }
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
@ -58,10 +58,17 @@ object eval(const str &expr, object global = globals(), object local = object())
int start = 0; int start = 0;
switch (mode) { switch (mode) {
case eval_expr: start = Py_eval_input; break; case eval_expr:
case eval_single_statement: start = Py_single_input; break; start = Py_eval_input;
case eval_statements: start = Py_file_input; break; break;
default: pybind11_fail("invalid evaluation mode"); 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()); PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
@ -74,8 +81,7 @@ object eval(const str &expr, object global = globals(), object local = object())
template <eval_mode mode = eval_expr, size_t N> template <eval_mode mode = eval_expr, size_t N>
object eval(const char (&s)[N], object global = globals(), object local = object()) { object eval(const char (&s)[N], object global = globals(), object local = object()) {
/* Support raw string literals by removing common leading whitespace */ /* Support raw string literals by removing common leading whitespace */
auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) : str(s);
: str(s);
return eval<mode>(expr, global, local); return eval<mode>(expr, global, local);
} }
@ -112,28 +118,34 @@ object eval_file(str fname, object global = globals(), object local = object())
int start = 0; int start = 0;
switch (mode) { switch (mode) {
case eval_expr: start = Py_eval_input; break; case eval_expr:
case eval_single_statement: start = Py_single_input; break; start = Py_eval_input;
case eval_statements: start = Py_file_input; break; break;
default: pybind11_fail("invalid evaluation mode"); 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; int closeFile = 1;
std::string fname_str = (std::string) fname; 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"); 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"); FILE *f = _Py_fopen(fname.ptr(), "r");
#else # else
/* No unicode support in open() :( */ /* No unicode support in open() :( */
auto fobj = reinterpret_steal<object>(PyFile_FromString( auto fobj = reinterpret_steal<object>(
const_cast<char *>(fname_str.c_str()), PyFile_FromString(const_cast<char *>(fname_str.c_str()), const_cast<char *>("r")));
const_cast<char*>("r")));
FILE *f = nullptr; FILE *f = nullptr;
if (fobj) if (fobj)
f = PyFile_AsFile(fobj.ptr()); f = PyFile_AsFile(fobj.ptr());
closeFile = 0; closeFile = 0;
#endif # endif
if (!f) { if (!f) {
PyErr_Clear(); PyErr_Clear();
pybind11_fail("File \"" + fname_str + "\" could not be opened!"); pybind11_fail("File \"" + fname_str + "\" could not be opened!");
@ -142,20 +154,19 @@ object eval_file(str fname, object global = globals(), object local = object())
// In Python2, this should be encoded by getfilesystemencoding. // In Python2, this should be encoded by getfilesystemencoding.
// We don't boher setting it since Python2 is past EOL anyway. // We don't boher setting it since Python2 is past EOL anyway.
// See PR#3233 // See PR#3233
#if PY_VERSION_HEX >= 0x03000000 # if PY_VERSION_HEX >= 0x03000000
if (!global.contains("__file__")) { if (!global.contains("__file__")) {
global["__file__"] = std::move(fname); global["__file__"] = std::move(fname);
} }
#endif # endif
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) # if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), local.ptr());
local.ptr());
(void) closeFile; (void) closeFile;
#else # else
PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), PyObject *result
local.ptr(), closeFile); = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile);
#endif # endif
if (!result) { if (!result) {
throw error_already_set(); throw error_already_set();

13
include/pybind11/functional.h

@ -10,6 +10,7 @@
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include <functional> #include <functional>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@ -19,7 +20,7 @@ template <typename Return, typename... Args>
struct type_caster<std::function<Return(Args...)>> { struct type_caster<std::function<Return(Args...)>> {
using type = 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 retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
using function_type = Return (*) (Args...); using function_type = Return (*)(Args...);
public: public:
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
@ -76,7 +77,9 @@ public:
// This triggers a syntax error under very special conditions (very weird indeed). // This triggers a syntax error under very special conditions (very weird indeed).
explicit explicit
#endif #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(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) { func_handle &operator=(const func_handle &f_) {
gil_scoped_acquire acq; gil_scoped_acquire acq;
@ -118,8 +121,10 @@ public:
return cpp_function(std::forward<Func>(f_), policy).release(); return cpp_function(std::forward<Func>(f_), policy).release();
} }
PYBIND11_TYPE_CASTER(type, const_name("Callable[[") + concat(make_caster<Args>::name...) + const_name("], ") PYBIND11_TYPE_CASTER(type,
+ make_caster<retval_type>::name + const_name("]")); const_name("Callable[[") + concat(make_caster<Args>::name...)
+ const_name("], ") + make_caster<retval_type>::name
+ const_name("]"));
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

30
include/pybind11/gil.h

@ -14,7 +14,6 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
// forward declarations // forward declarations
@ -22,7 +21,6 @@ PyThreadState *get_thread_state_unchecked();
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
#if defined(WITH_THREAD) && !defined(PYPY_VERSION) #if defined(WITH_THREAD) && !defined(PYPY_VERSION)
/* The functions below essentially reproduce the PyGILState_* API using a RAII /* The functions below essentially reproduce the PyGILState_* API using a RAII
@ -64,11 +62,11 @@ public:
if (!tstate) { if (!tstate) {
tstate = PyThreadState_New(internals.istate); tstate = PyThreadState_New(internals.istate);
#if !defined(NDEBUG) # if !defined(NDEBUG)
if (!tstate) { if (!tstate) {
pybind11_fail("scoped_acquire: could not create thread state!"); pybind11_fail("scoped_acquire: could not create thread state!");
} }
#endif # endif
tstate->gilstate_counter = 0; tstate->gilstate_counter = 0;
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
} else { } else {
@ -82,26 +80,24 @@ public:
inc_ref(); inc_ref();
} }
void inc_ref() { void inc_ref() { ++tstate->gilstate_counter; }
++tstate->gilstate_counter;
}
PYBIND11_NOINLINE void dec_ref() { PYBIND11_NOINLINE void dec_ref() {
--tstate->gilstate_counter; --tstate->gilstate_counter;
#if !defined(NDEBUG) # if !defined(NDEBUG)
if (detail::get_thread_state_unchecked() != tstate) { if (detail::get_thread_state_unchecked() != tstate) {
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
} }
if (tstate->gilstate_counter < 0) { if (tstate->gilstate_counter < 0) {
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
} }
#endif # endif
if (tstate->gilstate_counter == 0) { if (tstate->gilstate_counter == 0) {
#if !defined(NDEBUG) # if !defined(NDEBUG)
if (!release) { if (!release) {
pybind11_fail("scoped_acquire::dec_ref(): internal error!"); pybind11_fail("scoped_acquire::dec_ref(): internal error!");
} }
#endif # endif
PyThreadState_Clear(tstate); PyThreadState_Clear(tstate);
if (active) { if (active) {
PyThreadState_DeleteCurrent(); PyThreadState_DeleteCurrent();
@ -116,9 +112,7 @@ public:
/// could be shutting down when this is called, as thread deletion is not /// could be shutting down when this is called, as thread deletion is not
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
/// protect subsequent code. /// protect subsequent code.
PYBIND11_NOINLINE void disarm() { PYBIND11_NOINLINE void disarm() { active = false; }
active = false;
}
PYBIND11_NOINLINE ~gil_scoped_acquire() { PYBIND11_NOINLINE ~gil_scoped_acquire() {
dec_ref(); dec_ref();
@ -126,6 +120,7 @@ public:
PyEval_SaveThread(); PyEval_SaveThread();
} }
} }
private: private:
PyThreadState *tstate = nullptr; PyThreadState *tstate = nullptr;
bool release = true; bool release = true;
@ -154,9 +149,7 @@ public:
/// could be shutting down when this is called, as thread deletion is not /// could be shutting down when this is called, as thread deletion is not
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
/// protect subsequent code. /// protect subsequent code.
PYBIND11_NOINLINE void disarm() { PYBIND11_NOINLINE void disarm() { active = false; }
active = false;
}
~gil_scoped_release() { ~gil_scoped_release() {
if (!tstate) { if (!tstate) {
@ -173,6 +166,7 @@ public:
PYBIND11_TLS_REPLACE_VALUE(key, tstate); PYBIND11_TLS_REPLACE_VALUE(key, tstate);
} }
} }
private: private:
PyThreadState *tstate; PyThreadState *tstate;
bool disassoc; bool disassoc;
@ -181,6 +175,7 @@ private:
#elif defined(PYPY_VERSION) #elif defined(PYPY_VERSION)
class gil_scoped_acquire { class gil_scoped_acquire {
PyGILState_STATE state; PyGILState_STATE state;
public: public:
gil_scoped_acquire() { state = PyGILState_Ensure(); } gil_scoped_acquire() { state = PyGILState_Ensure(); }
~gil_scoped_acquire() { PyGILState_Release(state); } ~gil_scoped_acquire() { PyGILState_Release(state); }
@ -189,6 +184,7 @@ public:
class gil_scoped_release { class gil_scoped_release {
PyThreadState *state; PyThreadState *state;
public: public:
gil_scoped_release() { state = PyEval_SaveThread(); } gil_scoped_release() { state = PyEval_SaveThread(); }
~gil_scoped_release() { PyEval_RestoreThread(state); } ~gil_scoped_release() { PyEval_RestoreThread(state); }

41
include/pybind11/iostream.h

@ -58,31 +58,23 @@ private:
size_t utf8_remainder() const { size_t utf8_remainder() const {
const auto rbase = std::reverse_iterator<char *>(pbase()); const auto rbase = std::reverse_iterator<char *>(pbase());
const auto rpptr = std::reverse_iterator<char *>(pptr()); const auto rpptr = std::reverse_iterator<char *>(pptr());
auto is_ascii = [](char c) { auto is_ascii = [](char c) { return (static_cast<unsigned char>(c) & 0x80) == 0x00; };
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 = [](char c) { auto is_leading_3b = [](char c) { return static_cast<unsigned char>(c) <= 0xEF; };
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 the last character is ASCII, there are no incomplete code points
if (is_ascii(*rpptr)) { if (is_ascii(*rpptr)) {
return 0; return 0;
} }
// Otherwise, work back from the end of the buffer and find the first // Otherwise, work back from the end of the buffer and find the first
// UTF-8 leading byte // 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); const auto leading = std::find_if(rpptr, rpend, is_leading);
if (leading == rbase) { if (leading == rbase) {
return 0; return 0;
} }
const auto dist = static_cast<size_t>(leading - rpptr); const auto dist = static_cast<size_t>(leading - rpptr);
size_t remainder = 0; size_t remainder = 0;
if (dist == 0) { if (dist == 0) {
remainder = 1; // 1-byte code point is impossible remainder = 1; // 1-byte code point is impossible
@ -103,7 +95,7 @@ private:
if (pbase() != pptr()) { // If buffer is not empty if (pbase() != pptr()) { // If buffer is not empty
gil_scoped_acquire tmp; gil_scoped_acquire tmp;
// This subtraction cannot be negative, so dropping the sign. // 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(); size_t remainder = utf8_remainder();
if (size > remainder) { if (size > remainder) {
@ -122,9 +114,7 @@ private:
return 0; return 0;
} }
int sync() override { int sync() override { return _sync(); }
return _sync();
}
public: public:
explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024) explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024)
@ -133,17 +123,14 @@ public:
setp(d_buffer.get(), d_buffer.get() + buf_size - 1); setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
} }
pythonbuf(pythonbuf&&) = default; pythonbuf(pythonbuf &&) = default;
/// Sync before destroy /// Sync before destroy
~pythonbuf() override { ~pythonbuf() override { _sync(); }
_sync();
}
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/** \rst /** \rst
This a move-only guard that redirects output. This a move-only guard that redirects output.
@ -183,9 +170,7 @@ public:
old = costream.rdbuf(&buffer); old = costream.rdbuf(&buffer);
} }
~scoped_ostream_redirect() { ~scoped_ostream_redirect() { costream.rdbuf(old); }
costream.rdbuf(old);
}
scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; scoped_ostream_redirect(const scoped_ostream_redirect &) = delete;
scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; scoped_ostream_redirect(scoped_ostream_redirect &&other) = default;
@ -193,7 +178,6 @@ public:
scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete;
}; };
/** \rst /** \rst
Like `scoped_ostream_redirect`, but redirects cerr by default. This class Like `scoped_ostream_redirect`, but redirects cerr by default. This class
is provided primary to make ``py::call_guard`` easier to make. is provided primary to make ``py::call_guard`` easier to make.
@ -213,7 +197,6 @@ public:
: scoped_ostream_redirect(costream, pyostream) {} : scoped_ostream_redirect(costream, pyostream) {}
}; };
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
// Class to redirect output as a context manager. C++ backend. // 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)
/// Enumeration with all supported operator types /// Enumeration with all supported operator types
enum op_id : int { enum op_id : int {
op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, op_add,
op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, op_sub,
op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, op_mul,
op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, op_div,
op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, op_mod,
op_repr, op_truediv, op_itruediv, op_hash 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 { enum op_type : int {
@ -30,126 +68,145 @@ enum op_type : int {
op_u /* unary operator */ op_u /* unary operator */
}; };
struct self_t { }; struct self_t {};
static const self_t self = self_t(); static const self_t self = self_t();
/// Type for an unused type slot /// Type for an unused type slot
struct undefined_t { }; struct undefined_t {};
/// Don't warn about an unused variable /// Don't warn about an unused variable
inline self_t __self() { return self; } inline self_t __self() { return self; }
/// base template of operator implementations /// 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 /// Operator implementation generator
template <op_id id, op_type ot, typename L, typename R> struct op_ { template <op_id id, op_type ot, typename L, typename R>
template <typename Class, typename... Extra> void execute(Class &cl, const Extra&... extra) const { struct op_ {
template <typename Class, typename... Extra>
void execute(Class &cl, const Extra &...extra) const {
using Base = typename Class::type; using Base = typename Class::type;
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>; 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 R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
using op = op_impl<id, ot, Base, L_type, R_type>; using op = op_impl<id, ot, Base, L_type, R_type>;
cl.def(op::name(), &op::execute, is_operator(), extra...); cl.def(op::name(), &op::execute, is_operator(), extra...);
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv) || if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv)
PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv)) || PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", cl.def(id == op_itruediv ? "__idiv__"
&op::execute, is_operator(), extra...); : ot == op_l ? "__div__"
#endif : "__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 Base = typename Class::type;
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>; 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 R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
using op = op_impl<id, ot, Base, L_type, R_type>; using op = op_impl<id, ot, Base, L_type, R_type>;
cl.def(op::name(), &op::execute_cast, is_operator(), extra...); 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) if (id == op_truediv || id == op_itruediv)
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", cl.def(id == op_itruediv ? "__idiv__"
&op::execute, is_operator(), extra...); : ot == op_l ? "__div__"
#endif : "__rdiv__",
&op::execute,
is_operator(),
extra...);
#endif
} }
}; };
#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ #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> { \ template <typename B, typename L, typename R> \
static char const* name() { return "__" #id "__"; } \ struct op_impl<op_##id, op_l, B, L, R> { \
static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ static char const *name() { return "__" #id "__"; } \
static B execute_cast(const L &l, const R &r) { return B(expr); } \ 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 "__"; } \ template <typename B, typename L, typename R> \
static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ struct op_impl<op_##id, op_r, B, L, R> { \
static B execute_cast(const R &r, const L &l) { return B(expr); } \ static char const *name() { return "__" #rid "__"; } \
}; \ static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \
inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) { \ static B execute_cast(const R &r, const L &l) { return B(expr); } \
return op_<op_##id, op_l, self_t, self_t>(); \ }; \
} \ inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const 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, self_t>(); \
return op_<op_##id, op_l, self_t, T>(); \ } \
} \ template <typename T> \
template <typename T> op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) { \ op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
return op_<op_##id, op_r, T, self_t>(); \ return op_<op_##id, op_l, self_t, T>(); \
} } \
template <typename T> \
#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) { \
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \ return op_<op_##id, op_r, T, self_t>(); \
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); } \ #define PYBIND11_INPLACE_OPERATOR(id, op, expr) \
}; \ template <typename B, typename L, typename R> \
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \ struct op_impl<op_##id, op_l, B, L, R> { \
return op_<op_##id, op_l, self_t, T>(); \ 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); } \
#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ }; \
template <typename B, typename L> struct op_impl<op_##id, op_u, B, L, undefined_t> { \ template <typename T> \
static char const* name() { return "__" #id "__"; } \ op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
static auto execute(const L &l) -> decltype(expr) { return expr; } \ return op_<op_##id, op_l, self_t, T>(); \
static B execute_cast(const L &l) { return B(expr); } \ }
}; \
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \ #define PYBIND11_UNARY_OPERATOR(id, op, expr) \
return op_<op_##id, op_u, self_t, undefined_t>(); \ template <typename B, typename L> \
} struct op_impl<op_##id, op_u, B, L, undefined_t> { \
static char const *name() { return "__" #id "__"; } \
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) static auto execute(const L &l) -> decltype(expr) { return expr; } \
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) static B execute_cast(const L &l) { return B(expr); } \
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) }; \
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) return op_<op_##id, op_u, self_t, undefined_t>(); \
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(sub, rsub, operator-, l - r)
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l *r)
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) PYBIND11_BINARY_OPERATOR(and, rand, operator&, l &r)
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) // PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
PYBIND11_UNARY_OPERATOR(neg, operator-, -l) PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
PYBIND11_UNARY_OPERATOR(pos, operator+, +l) 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. // WARNING: This usage of `abs` should only be done for existing STL overloads.
// Adding overloads directly in to the `std::` namespace is advised against: // Adding overloads directly in to the `std::` namespace is advised against:
// https://en.cppreference.com/w/cpp/language/extending_std // https://en.cppreference.com/w/cpp/language/extending_std
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l)) PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
PYBIND11_UNARY_OPERATOR(int, int_, (int) l) PYBIND11_UNARY_OPERATOR(int, int_, (int) l)
PYBIND11_UNARY_OPERATOR(float, float_, (double) l) PYBIND11_UNARY_OPERATOR(float, float_, (double) l)
#undef PYBIND11_BINARY_OPERATOR #undef PYBIND11_BINARY_OPERATOR
#undef PYBIND11_INPLACE_OPERATOR #undef PYBIND11_INPLACE_OPERATOR

42
include/pybind11/options.h

@ -15,44 +15,54 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
class options { class options {
public: public:
// Default RAII constructor, which leaves settings as they currently are. // Default RAII constructor, which leaves settings as they currently are.
options() : previous_state(global_state()) {} options() : previous_state(global_state()) {}
// Class is non-copyable. // Class is non-copyable.
options(const options&) = delete; options(const options &) = delete;
options& operator=(const options&) = delete; options &operator=(const options &) = delete;
// Destructor, which restores settings that were in effect before. // Destructor, which restores settings that were in effect before.
~options() { ~options() { global_state() = previous_state; }
global_state() = previous_state;
}
// Setter methods (affect the global 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): // 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; } static bool show_function_signatures() { return global_state().show_function_signatures; }
// This type is not meant to be allocated on the heap. // This type is not meant to be allocated on the heap.
void* operator new(size_t) = delete; void *operator new(size_t) = delete;
private: private:
struct state { struct state {
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
bool show_function_signatures = true; //< Include auto-generated function signatures bool show_function_signatures = true; //< Include auto-generated function signatures
// in docstrings. // in docstrings.
}; };
static state &global_state() { 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 @@
#pragma once #pragma once
#include "detail/common.h"
#include "pybind11.h" #include "pybind11.h"
#include <set> #include "detail/common.h"
#include <unordered_set>
#include <map> #include <deque>
#include <unordered_map>
#include <iostream> #include <iostream>
#include <list> #include <list>
#include <deque> #include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <valarray> #include <valarray>
// See `detail/common.h` for implementation of these guards. // See `detail/common.h` for implementation of these guards.
#if defined(PYBIND11_HAS_OPTIONAL) #if defined(PYBIND11_HAS_OPTIONAL)
# include <optional> # include <optional>
#elif defined(PYBIND11_HAS_EXP_OPTIONAL) #elif defined(PYBIND11_HAS_EXP_OPTIONAL)
# include <experimental/optional> # include <experimental/optional>
#endif #endif
#if defined(PYBIND11_HAS_VARIANT) #if defined(PYBIND11_HAS_VARIANT)
# include <variant> # include <variant>
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@ -37,8 +38,9 @@ PYBIND11_NAMESPACE_BEGIN(detail)
/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for /// 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. /// forwarding a container element). Typically used indirect via forwarded_type(), below.
template <typename T, typename U> template <typename T, typename U>
using forwarded_type = conditional_t< using forwarded_type = conditional_t<std::is_lvalue_reference<T>::value,
std::is_lvalue_reference<T>::value, remove_reference_t<U> &, remove_reference_t<U> &&>; 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 /// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically
/// used for forwarding a container's elements. /// used for forwarding a container's elements.
@ -47,7 +49,8 @@ forwarded_type<T, U> forward_like(U &&u) {
return std::forward<detail::forwarded_type<T, U>>(std::forward<U>(u)); 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 type = Type;
using key_conv = make_caster<Key>; using key_conv = make_caster<Key>;
@ -74,7 +77,8 @@ template <typename Type, typename Key> struct set_caster {
} }
pybind11::set s; pybind11::set s;
for (auto &&value : src) { 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_)) { if (!value_ || !s.add(value_)) {
return handle(); return handle();
} }
@ -85,8 +89,9 @@ template <typename Type, typename Key> struct set_caster {
PYBIND11_TYPE_CASTER(type, const_name("Set[") + key_conv::name + const_name("]")); PYBIND11_TYPE_CASTER(type, const_name("Set[") + key_conv::name + const_name("]"));
}; };
template <typename Type, typename Key, typename Value> struct map_caster { template <typename Type, typename Key, typename Value>
using key_conv = make_caster<Key>; struct map_caster {
using key_conv = make_caster<Key>;
using value_conv = make_caster<Value>; using value_conv = make_caster<Value>;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
@ -116,8 +121,10 @@ template <typename Type, typename Key, typename Value> struct map_caster {
policy_value = return_value_policy_override<Value>::policy(policy_value); policy_value = return_value_policy_override<Value>::policy(policy_value);
} }
for (auto &&kv : src) { for (auto &&kv : src) {
auto key = reinterpret_steal<object>(key_conv::cast(forward_like<T>(kv.first), policy_key, parent)); auto key = reinterpret_steal<object>(
auto value = reinterpret_steal<object>(value_conv::cast(forward_like<T>(kv.second), policy_value, parent)); 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) { if (!key || !value) {
return handle(); return handle();
} }
@ -126,10 +133,13 @@ template <typename Type, typename Key, typename Value> struct map_caster {
return d.release(); 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>; using value_conv = make_caster<Value>;
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
@ -151,7 +161,7 @@ template <typename Type, typename Value> struct list_caster {
private: private:
template < template <
typename T = Type, typename T = Type,
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0> enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
void reserve_maybe(const sequence &s, Type *) { void reserve_maybe(const sequence &s, Type *) {
value.reserve(s.size()); value.reserve(s.size());
@ -167,7 +177,8 @@ public:
list l(src.size()); list l(src.size());
ssize_t index = 0; ssize_t index = 0;
for (auto &&value : src) { 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_) { if (!value_) {
return handle(); return handle();
} }
@ -179,16 +190,17 @@ public:
PYBIND11_TYPE_CASTER(Type, const_name("List[") + value_conv::name + const_name("]")); PYBIND11_TYPE_CASTER(Type, const_name("List[") + value_conv::name + const_name("]"));
}; };
template <typename Type, typename Alloc> struct type_caster<std::vector<Type, Alloc>> template <typename Type, typename Alloc>
: list_caster<std::vector<Type, Alloc>, Type> { }; 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>> template <typename Type, typename Alloc>
: list_caster<std::deque<Type, Alloc>, Type> { }; 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>> template <typename Type, typename Alloc>
: list_caster<std::list<Type, Alloc>, Type> { }; 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>; using value_conv = make_caster<Value>;
private: private:
@ -229,7 +241,8 @@ public:
list l(src.size()); list l(src.size());
ssize_t index = 0; ssize_t index = 0;
for (auto &&value : src) { 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_) { if (!value_) {
return handle(); return handle();
} }
@ -238,29 +251,40 @@ public:
return l.release(); 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>> template <typename Type, size_t Size>
: array_caster<std::array<Type, Size>, Type, false, 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>> template <typename Type>
: array_caster<std::valarray<Type>, Type, true> { }; 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>> template <typename Key, typename Compare, typename Alloc>
: set_caster<std::set<Key, Compare, Alloc>, Key> { }; 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>> template <typename Key, typename Hash, typename Equal, typename Alloc>
: set_caster<std::unordered_set<Key, Hash, Equal, Alloc>, Key> { }; 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>> template <typename Key, typename Value, typename Compare, typename Alloc>
: map_caster<std::map<Key, Value, Compare, Alloc>, Key, Value> { }; 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>> template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc>
: map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> { }; 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 // 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>; using value_conv = make_caster<Value>;
template <typename T> template <typename T>
@ -279,7 +303,7 @@ template<typename Type, typename Value = typename Type::value_type> struct optio
return false; return false;
} }
if (src.is_none()) { if (src.is_none()) {
return true; // default-constructed value is already empty return true; // default-constructed value is already empty
} }
value_conv inner_caster; value_conv inner_caster;
if (!inner_caster.load(src, convert)) { if (!inner_caster.load(src, convert)) {
@ -294,18 +318,20 @@ template<typename Type, typename Value = typename Type::value_type> struct optio
}; };
#if defined(PYBIND11_HAS_OPTIONAL) #if defined(PYBIND11_HAS_OPTIONAL)
template<typename T> struct type_caster<std::optional<T>> template <typename T>
: public optional_caster<std::optional<T>> {}; struct type_caster<std::optional<T>> : public optional_caster<std::optional<T>> {};
template<> struct type_caster<std::nullopt_t> template <>
: public void_caster<std::nullopt_t> {}; struct type_caster<std::nullopt_t> : public void_caster<std::nullopt_t> {};
#endif #endif
#if defined(PYBIND11_HAS_EXP_OPTIONAL) #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>> {}; : 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> {}; : public void_caster<std::experimental::nullopt_t> {};
#endif #endif
@ -326,7 +352,7 @@ struct variant_caster_visitor {
/// `namespace::variant` types which provide a `namespace::visit()` function are handled here /// `namespace::variant` types which provide a `namespace::visit()` function are handled here
/// automatically using argument-dependent lookup. Users can provide specializations for other /// automatically using argument-dependent lookup. Users can provide specializations for other
/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. /// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`.
template <template<typename...> class Variant> template <template <typename...> class Variant>
struct visit_helper { struct visit_helper {
template <typename... Args> template <typename... Args>
static auto call(Args &&...args) -> decltype(visit(std::forward<Args>(args)...)) { static auto call(Args &&...args) -> decltype(visit(std::forward<Args>(args)...)) {
@ -335,9 +361,10 @@ struct visit_helper {
}; };
/// Generic variant caster /// 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...>> { struct variant_caster<V<Ts...>> {
static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative.");
@ -371,12 +398,14 @@ struct variant_caster<V<Ts...>> {
} }
using Type = V<Ts...>; 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) #if defined(PYBIND11_HAS_VARIANT)
template <typename... Ts> 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 #endif
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

46
include/pybind11/stl/filesystem.h

@ -4,21 +4,20 @@
#pragma once #pragma once
#include "../cast.h"
#include "../pybind11.h" #include "../pybind11.h"
#include "../pytypes.h"
#include "../detail/common.h" #include "../detail/common.h"
#include "../detail/descr.h" #include "../detail/descr.h"
#include "../cast.h"
#include "../pytypes.h"
#include <string> #include <string>
#ifdef __has_include #ifdef __has_include
# if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \ # if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \
PY_VERSION_HEX >= 0x03060000 PY_VERSION_HEX >= 0x03060000
# include <filesystem> # include <filesystem>
# define PYBIND11_HAS_FILESYSTEM 1 # define PYBIND11_HAS_FILESYSTEM 1
# endif # endif
#endif #endif
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL) #if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
@ -30,28 +29,29 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if defined(PYBIND11_HAS_FILESYSTEM) #if defined(PYBIND11_HAS_FILESYSTEM)
template<typename T> struct path_caster { template <typename T>
struct path_caster {
private: private:
static PyObject* unicode_from_fs_native(const std::string& w) { static PyObject *unicode_from_fs_native(const std::string &w) {
#if !defined(PYPY_VERSION) # if !defined(PYPY_VERSION)
return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size())); return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size()));
#else # else
// PyPy mistakenly declares the first parameter as non-const. // PyPy mistakenly declares the first parameter as non-const.
return PyUnicode_DecodeFSDefaultAndSize( return PyUnicode_DecodeFSDefaultAndSize(const_cast<char *>(w.c_str()), ssize_t(w.size()));
const_cast<char*>(w.c_str()), ssize_t(w.size())); # endif
#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())); return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size()));
} }
public: 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())) { if (auto py_str = unicode_from_fs_native(path.native())) {
return module_::import("pathlib").attr("Path")(reinterpret_steal<object>(py_str)) return module_::import("pathlib")
.release(); .attr("Path")(reinterpret_steal<object>(py_str))
.release();
} }
return nullptr; return nullptr;
} }
@ -60,12 +60,12 @@ public:
// PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of // PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of
// calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy // calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy
// issue #3168) so we do it ourselves instead. // issue #3168) so we do it ourselves instead.
PyObject* buf = PyOS_FSPath(handle.ptr()); PyObject *buf = PyOS_FSPath(handle.ptr());
if (!buf) { if (!buf) {
PyErr_Clear(); PyErr_Clear();
return false; return false;
} }
PyObject* native = nullptr; PyObject *native = nullptr;
if constexpr (std::is_same_v<typename T::value_type, char>) { if constexpr (std::is_same_v<typename T::value_type, char>) {
if (PyUnicode_FSConverter(buf, &native) != 0) { if (PyUnicode_FSConverter(buf, &native) != 0) {
if (auto *c_str = PyBytes_AsString(native)) { if (auto *c_str = PyBytes_AsString(native)) {
@ -78,7 +78,7 @@ public:
if (PyUnicode_FSDecoder(buf, &native) != 0) { if (PyUnicode_FSDecoder(buf, &native) != 0) {
if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) { if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) {
// AsWideCharString returns a new string that must be free'd. // 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); PyMem_Free(c_str);
} }
} }
@ -95,8 +95,8 @@ public:
PYBIND11_TYPE_CASTER(T, const_name("os.PathLike")); PYBIND11_TYPE_CASTER(T, const_name("os.PathLike"));
}; };
template<> struct type_caster<std::filesystem::path> template <>
: public path_caster<std::filesystem::path> {}; struct type_caster<std::filesystem::path> : public path_caster<std::filesystem::path> {};
#endif // PYBIND11_HAS_FILESYSTEM #endif // PYBIND11_HAS_FILESYSTEM
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

465
include/pybind11/stl_bind.h

@ -19,74 +19,87 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
/* SFINAE helper class used by 'is_comparable */ /* SFINAE helper class used by 'is_comparable */
template <typename T> struct container_traits { template <typename T>
template <typename T2> static std::true_type test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>())*); struct container_traits {
template <typename T2> static std::false_type test_comparable(...); template <typename T2>
template <typename T2> static std::true_type test_value(typename T2::value_type *); static std::true_type
template <typename T2> static std::false_type test_value(...); test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>()) *);
template <typename T2> static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); template <typename T2>
template <typename T2> static std::false_type test_pair(...); static std::false_type test_comparable(...);
template <typename T2>
static constexpr const bool is_comparable = std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value; static std::true_type test_value(typename T2::value_type *);
static constexpr const bool is_pair = std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value; template <typename T2>
static constexpr const bool is_vector = std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value; 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; static constexpr const bool is_element = !is_pair && !is_vector;
}; };
/* Default: is_comparable -> std::false_type */ /* Default: is_comparable -> std::false_type */
template <typename T, typename SFINAE = void> 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 */ /* For non-map data structures, check whether operator== can be instantiated */
template <typename T> template <typename T>
struct is_comparable< struct is_comparable<
T, enable_if_t<container_traits<T>::is_element && T,
container_traits<T>::is_comparable>> enable_if_t<container_traits<T>::is_element && container_traits<T>::is_comparable>>
: std::true_type { }; : std::true_type {};
/* For a vector/map data structure, recursively check the value type /* For a vector/map data structure, recursively check the value type
(which is std::pair for maps) */ (which is std::pair for maps) */
template <typename T> template <typename T>
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> { struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> {
static constexpr const bool value = static constexpr const bool value = is_comparable<typename T::value_type>::value;
is_comparable<typename T::value_type>::value;
}; };
/* For pairs, recursively check the two data types */ /* For pairs, recursively check the two data types */
template <typename T> template <typename T>
struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> { struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> {
static constexpr const bool value = static constexpr const bool value = is_comparable<typename T::first_type>::value
is_comparable<typename T::first_type>::value && && is_comparable<typename T::second_type>::value;
is_comparable<typename T::second_type>::value;
}; };
/* Fallback functions */ /* Fallback functions */
template <typename, typename, typename... Args> void vector_if_copy_constructible(const Args &...) { } template <typename, typename, typename... Args>
template <typename, typename, typename... Args> void vector_if_equal_operator(const Args &...) { } void vector_if_copy_constructible(const Args &...) {}
template <typename, typename, typename... Args> void vector_if_insertion_operator(const Args &...) { } template <typename, typename, typename... Args>
template <typename, typename, typename... Args> void vector_modifiers(const 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) { void vector_if_copy_constructible(enable_if_t<is_copy_constructible<Vector>::value, Class_> &cl) {
cl.def(init<const Vector &>(), "Copy constructor"); 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) { void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_> &cl) {
using T = typename Vector::value_type; using T = typename Vector::value_type;
cl.def(self == self); cl.def(self == self);
cl.def(self != self); cl.def(self != self);
cl.def("count", cl.def(
[](const Vector &v, const T &x) { "count",
return std::count(v.begin(), v.end(), x); [](const Vector &v, const T &x) { return std::count(v.begin(), v.end(), x); },
},
arg("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); auto p = std::find(v.begin(), v.end(), x);
if (p != v.end()) { if (p != v.end()) {
v.erase(p); v.erase(p);
@ -96,23 +109,21 @@ void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_>
}, },
arg("x"), arg("x"),
"Remove the first item from the list whose value is 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__", cl.def(
[](const Vector &v, const T &x) { "__contains__",
return std::find(v.begin(), v.end(), x) != v.end(); [](const Vector &v, const T &x) { return std::find(v.begin(), v.end(), x) != v.end(); },
},
arg("x"), arg("x"),
"Return true the container contains ``x``" "Return true the container contains ``x``");
);
} }
// Vector modifiers -- requires a copyable vector_type: // Vector modifiers -- requires a copyable vector_type:
// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems // (Technically, some of these (pop and __delitem__) don't actually require copyability, but it
// silly to allow deletion but not insertion, so include them here too.) // seems silly to allow deletion but not insertion, so include them here too.)
template <typename Vector, typename Class_> 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 T = typename Vector::value_type;
using SizeType = typename Vector::size_type; using SizeType = typename Vector::size_type;
using DiffType = typename Vector::difference_type; using DiffType = typename Vector::difference_type;
@ -127,10 +138,11 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
return i; return i;
}; };
cl.def("append", cl.def(
[](Vector &v, const T &value) { v.push_back(value); }, "append",
arg("x"), [](Vector &v, const T &value) { v.push_back(value); },
"Add an item to the end of the list"); arg("x"),
"Add an item to the end of the list");
cl.def(init([](const iterable &it) { cl.def(init([](const iterable &it) {
auto v = std::unique_ptr<Vector>(new Vector()); auto v = std::unique_ptr<Vector>(new Vector());
@ -141,20 +153,14 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
return v.release(); return v.release();
})); }));
cl.def("clear", cl.def(
[](Vector &v) { "clear", [](Vector &v) { v.clear(); }, "Clear the contents");
v.clear();
},
"Clear the contents"
);
cl.def("extend", cl.def(
[](Vector &v, const Vector &src) { "extend",
v.insert(v.end(), src.begin(), src.end()); [](Vector &v, const Vector &src) { v.insert(v.end(), src.begin(), src.end()); },
}, arg("L"),
arg("L"), "Extend the list by appending all the items in the given list");
"Extend the list by appending all the items in the given list"
);
cl.def( cl.def(
"extend", "extend",
@ -179,7 +185,8 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
arg("L"), arg("L"),
"Extend the list by appending all the items in the given list"); "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) { [](Vector &v, DiffType i, const T &x) {
// Can't use wrap_i; i == v.size() is OK // Can't use wrap_i; i == v.size() is OK
if (i < 0) { if (i < 0) {
@ -190,9 +197,9 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
} }
v.insert(v.begin() + i, x); v.insert(v.begin() + i, x);
}, },
arg("i") , arg("x"), arg("i"),
"Insert an item at a given position." arg("x"),
); "Insert an item at a given position.");
cl.def( cl.def(
"pop", "pop",
@ -206,7 +213,8 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
}, },
"Remove and return the last item"); "Remove and return the last item");
cl.def("pop", cl.def(
"pop",
[wrap_i](Vector &v, DiffType i) { [wrap_i](Vector &v, DiffType i) {
i = wrap_i(i, v.size()); i = wrap_i(i, v.size());
T t = std::move(v[(SizeType) i]); T t = std::move(v[(SizeType) i]);
@ -214,15 +222,12 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
return t; return t;
}, },
arg("i"), arg("i"),
"Remove and return the item at index ``i``" "Remove and return the item at index ``i``");
);
cl.def("__setitem__", cl.def("__setitem__", [wrap_i](Vector &v, DiffType i, const T &t) {
[wrap_i](Vector &v, DiffType i, const T &t) { i = wrap_i(i, v.size());
i = wrap_i(i, v.size()); v[(SizeType) i] = t;
v[(SizeType)i] = t; });
}
);
/// Slicing protocol /// Slicing protocol
cl.def( cl.def(
@ -237,7 +242,7 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
auto *seq = new Vector(); auto *seq = new Vector();
seq->reserve((size_t) slicelength); 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]); seq->push_back(v[start]);
start += step; start += step;
} }
@ -259,20 +264,20 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
"Left and right hand size of slice assignment have different sizes!"); "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]; v[start] = value[i];
start += step; start += step;
} }
}, },
"Assign list elements using a slice object"); "Assign list elements using a slice object");
cl.def("__delitem__", cl.def(
"__delitem__",
[wrap_i](Vector &v, DiffType i) { [wrap_i](Vector &v, DiffType i) {
i = wrap_i(i, v.size()); i = wrap_i(i, v.size());
v.erase(v.begin() + i); v.erase(v.begin() + i);
}, },
"Delete the list elements at index ``i``" "Delete the list elements at index ``i``");
);
cl.def( cl.def(
"__delitem__", "__delitem__",
@ -297,8 +302,10 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
// If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>), // 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. // we have to access by copying; otherwise we return by reference.
template <typename Vector> using vector_needs_copy = negation< template <typename Vector>
std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), typename Vector::value_type &>>; 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 // The usual case: access and iterate by reference
template <typename Vector, typename Class_> template <typename Vector, typename Class_>
@ -306,7 +313,7 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl)
using T = typename Vector::value_type; using T = typename Vector::value_type;
using SizeType = typename Vector::size_type; using SizeType = typename Vector::size_type;
using DiffType = typename Vector::difference_type; using DiffType = typename Vector::difference_type;
using ItType = typename Vector::iterator; using ItType = typename Vector::iterator;
auto wrap_i = [](DiffType i, SizeType n) { auto wrap_i = [](DiffType i, SizeType n) {
if (i < 0) { if (i < 0) {
@ -318,21 +325,22 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl)
return i; return i;
}; };
cl.def("__getitem__", cl.def(
"__getitem__",
[wrap_i](Vector &v, DiffType i) -> T & { [wrap_i](Vector &v, DiffType i) -> T & {
i = wrap_i(i, v.size()); i = wrap_i(i, v.size());
return v[(SizeType)i]; return v[(SizeType) i];
}, },
return_value_policy::reference_internal // ref + keepalive return_value_policy::reference_internal // ref + keepalive
); );
cl.def("__iter__", cl.def(
[](Vector &v) { "__iter__",
return make_iterator< [](Vector &v) {
return_value_policy::reference_internal, ItType, ItType, T&>( return make_iterator<return_value_policy::reference_internal, ItType, ItType, T &>(
v.begin(), v.end()); v.begin(), v.end());
}, },
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
); );
} }
@ -342,7 +350,7 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl)
using T = typename Vector::value_type; using T = typename Vector::value_type;
using SizeType = typename Vector::size_type; using SizeType = typename Vector::size_type;
using DiffType = typename Vector::difference_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 { cl.def("__getitem__", [](const Vector &v, DiffType i) -> T {
if (i < 0 && (i += v.size()) < 0) { if (i < 0 && (i += v.size()) < 0) {
throw index_error(); throw index_error();
@ -353,25 +361,27 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl)
return v[(SizeType) i]; return v[(SizeType) i];
}); });
cl.def("__iter__", cl.def(
[](Vector &v) { "__iter__",
return make_iterator< [](Vector &v) {
return_value_policy::copy, ItType, ItType, T>( return make_iterator<return_value_policy::copy, ItType, ItType, T>(v.begin(), v.end());
v.begin(), v.end()); },
}, keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
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) template <typename Vector, typename Class_>
-> decltype(std::declval<std::ostream&>() << std::declval<typename Vector::value_type>(), void()) { 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; using size_type = typename Vector::size_type;
cl.def("__repr__", cl.def(
[name](Vector &v) { "__repr__",
[name](Vector &v) {
std::ostringstream s; std::ostringstream s;
s << name << '['; s << name << '[';
for (size_type i=0; i < v.size(); ++i) { for (size_type i = 0; i < v.size(); ++i) {
s << v[i]; s << v[i];
if (i != v.size() - 1) { if (i != v.size() - 1) {
s << ", "; s << ", ";
@ -380,8 +390,7 @@ template <typename Vector, typename Class_> auto vector_if_insertion_operator(Cl
s << ']'; s << ']';
return s.str(); 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 // Provide the buffer interface for vectors if we have data() and we have a format for it
@ -390,7 +399,11 @@ template <typename Vector, typename Class_> auto vector_if_insertion_operator(Cl
template <typename Vector, typename = void> template <typename Vector, typename = void>
struct vector_has_data_and_format : std::false_type {}; struct vector_has_data_and_format : std::false_type {};
template <typename Vector> 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(intel)] Separate function required here
// Workaround as the Intel compiler does not compile the enable_if_t part below // Workaround as the Intel compiler does not compile the enable_if_t part below
@ -405,17 +418,23 @@ constexpr bool args_any_are_buffer() {
// Add the buffer interface to a vector // Add the buffer interface to a vector
template <typename Vector, typename Class_, typename... Args> 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; 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 // 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 // at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here
format_descriptor<T>::format(); format_descriptor<T>::format();
cl.def_buffer([](Vector& v) -> buffer_info { 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)}); 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) { cl.def(init([](const buffer &buf) {
@ -429,7 +448,7 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
+ " C++: " + format_descriptor<T>::format() + ")"); + " 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)); ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
T *end = p + info.shape[0] * step; T *end = p + info.shape[0] * step;
if (step == 1) { if (step == 1) {
@ -441,18 +460,18 @@ void vector_buffer_impl(Class_& cl, std::true_type) {
vec.push_back(*p); vec.push_back(*p);
} }
return vec; return vec;
})); }));
return; return;
} }
template <typename Vector, typename Class_, typename... Args> 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> template <typename Vector, typename Class_, typename... Args>
void vector_buffer(Class_& cl) { void vector_buffer(Class_ &cl) {
vector_buffer_impl<Vector, Class_, Args...>(cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{}); vector_buffer_impl<Vector, Class_, Args...>(
cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{});
} }
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
@ -461,7 +480,7 @@ PYBIND11_NAMESPACE_END(detail)
// std::vector // std::vector
// //
template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args> 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>; using Class_ = class_<Vector, holder_type>;
// If the value_type is unregistered (e.g. a converting type) or is itself registered // If the value_type is unregistered (e.g. a converting type) or is itself registered
@ -492,18 +511,13 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
// Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive
detail::vector_accessor<Vector, Class_>(cl); detail::vector_accessor<Vector, Class_>(cl);
cl.def("__bool__", cl.def(
[](const Vector &v) -> bool { "__bool__",
return !v.empty(); [](const Vector &v) -> bool { return !v.empty(); },
}, "Check whether the list is nonempty");
"Check whether the list is nonempty"
);
cl.def("__len__", &Vector::size); cl.def("__len__", &Vector::size);
#if 0 #if 0
// C++ style functions deprecated, leaving it here as an example // C++ style functions deprecated, leaving it here as an example
cl.def(init<size_type>()); cl.def(init<size_type>());
@ -547,8 +561,6 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
return cl; return cl;
} }
// //
// std::map, std::unordered_map // std::map, std::unordered_map
// //
@ -556,55 +568,58 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
/* Fallback functions */ /* Fallback functions */
template <typename, typename, typename... Args> void map_if_insertion_operator(const Args &...) { } template <typename, typename, typename... Args>
template <typename, typename, typename... Args> void map_assignment(const 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 // Map assignment when copy-assignable: just copy the value
template <typename Map, typename Class_> 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 KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type; using MappedType = typename Map::mapped_type;
cl.def("__setitem__", cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
[](Map &m, const KeyType &k, const MappedType &v) { auto it = m.find(k);
auto it = m.find(k); if (it != m.end()) {
if (it != m.end()) { it->second = v;
it->second = v; } else {
} else { m.emplace(k, v);
m.emplace(k, v); }
} });
}
);
} }
// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting // Not copy-assignable, but still copy-constructible: we can update the value by erasing and
template<typename Map, typename Class_> // reinserting
void map_assignment(enable_if_t< template <typename Map, typename Class_>
!is_copy_assignable<typename Map::mapped_type>::value && void map_assignment(enable_if_t<!is_copy_assignable<typename Map::mapped_type>::value
is_copy_constructible<typename Map::mapped_type>::value, && is_copy_constructible<typename Map::mapped_type>::value,
Class_> &cl) { Class_> &cl) {
using KeyType = typename Map::key_type; using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type; using MappedType = typename Map::mapped_type;
cl.def("__setitem__", cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
[](Map &m, const KeyType &k, const MappedType &v) { // We can't use m[k] = v; because value type might not be default constructable
// We can't use m[k] = v; because value type might not be default constructable auto r = m.emplace(k, v);
auto r = m.emplace(k, v); if (!r.second) {
if (!r.second) { // value type is not copy assignable so the only way to insert it is to erase it
// value type is not copy assignable so the only way to insert it is to erase it first... // first...
m.erase(r.first); m.erase(r.first);
m.emplace(k, v); 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) cl.def(
-> decltype(std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void()) { "__repr__",
[name](Map &m) {
cl.def("__repr__",
[name](Map &m) {
std::ostringstream s; std::ostringstream s;
s << name << '{'; s << name << '{';
bool f = false; bool f = false;
@ -618,32 +633,28 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
s << '}'; s << '}';
return s.str(); return s.str();
}, },
"Return the canonical string representation of this map." "Return the canonical string representation of this map.");
);
} }
template<typename Map> template <typename Map>
struct keys_view struct keys_view {
{
Map &map; Map &map;
}; };
template<typename Map> template <typename Map>
struct values_view struct values_view {
{
Map &map; Map &map;
}; };
template<typename Map> template <typename Map>
struct items_view struct items_view {
{
Map &map; Map &map;
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args> 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 KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type; using MappedType = typename Map::mapped_type;
using KeysView = detail::keys_view<Map>; using KeysView = detail::keys_view<Map>;
@ -674,101 +685,97 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
// Register stream insertion operator (if possible) // Register stream insertion operator (if possible)
detail::map_if_insertion_operator<Map, Class_>(cl, name); detail::map_if_insertion_operator<Map, Class_>(cl, name);
cl.def("__bool__", cl.def(
"__bool__",
[](const Map &m) -> bool { return !m.empty(); }, [](const Map &m) -> bool { return !m.empty(); },
"Check whether the map is nonempty" "Check whether the map is nonempty");
);
cl.def("__iter__", cl.def(
[](Map &m) { return make_key_iterator(m.begin(), m.end()); }, "__iter__",
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */ [](Map &m) { return make_key_iterator(m.begin(), m.end()); },
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
); );
cl.def("keys", cl.def(
[](Map &m) { return KeysView{m}; }, "keys",
keep_alive<0, 1>() /* Essential: keep map alive while view exists */ [](Map &m) { return KeysView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def("values", cl.def(
[](Map &m) { return ValuesView{m}; }, "values",
keep_alive<0, 1>() /* Essential: keep map alive while view exists */ [](Map &m) { return ValuesView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def("items", cl.def(
[](Map &m) { return ItemsView{m}; }, "items",
keep_alive<0, 1>() /* Essential: keep map alive while view exists */ [](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 & { [](Map &m, const KeyType &k) -> MappedType & {
auto it = m.find(k); auto it = m.find(k);
if (it == m.end()) { if (it == m.end()) {
throw key_error(); throw key_error();
} }
return it->second; return it->second;
}, },
return_value_policy::reference_internal // ref + keepalive return_value_policy::reference_internal // ref + keepalive
); );
cl.def("__contains__", cl.def("__contains__", [](Map &m, const KeyType &k) -> bool {
[](Map &m, const KeyType &k) -> bool { auto it = m.find(k);
auto it = m.find(k); if (it == m.end()) {
if (it == m.end()) { return false;
return false;
}
return true;
} }
); return true;
});
// Fallback for when the object is not of the key type // Fallback for when the object is not of the key type
cl.def("__contains__", [](Map &, const object &) -> bool { return false; }); cl.def("__contains__", [](Map &, const object &) -> bool { return false; });
// Assignment provided only if the type is copyable // Assignment provided only if the type is copyable
detail::map_assignment<Map, Class_>(cl); detail::map_assignment<Map, Class_>(cl);
cl.def("__delitem__", cl.def("__delitem__", [](Map &m, const KeyType &k) {
[](Map &m, const KeyType &k) { auto it = m.find(k);
auto it = m.find(k); if (it == m.end()) {
if (it == m.end()) { throw key_error();
throw key_error(); }
} m.erase(it);
m.erase(it); });
}
);
cl.def("__len__", &Map::size); cl.def("__len__", &Map::size);
keys_view.def("__len__", [](KeysView &view) { return view.map.size(); }); keys_view.def("__len__", [](KeysView &view) { return view.map.size(); });
keys_view.def("__iter__", keys_view.def(
[](KeysView &view) { "__iter__",
return make_key_iterator(view.map.begin(), view.map.end()); [](KeysView &view) { return make_key_iterator(view.map.begin(), view.map.end()); },
},
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
); );
keys_view.def("__contains__", keys_view.def("__contains__", [](KeysView &view, const KeyType &k) -> bool {
[](KeysView &view, const KeyType &k) -> bool { auto it = view.map.find(k);
auto it = view.map.find(k); if (it == view.map.end()) {
if (it == view.map.end()) { return false;
return false;
}
return true;
} }
); return true;
});
// Fallback for when the object is not of the key type // Fallback for when the object is not of the key type
keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; }); keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; });
values_view.def("__len__", [](ValuesView &view) { return view.map.size(); }); values_view.def("__len__", [](ValuesView &view) { return view.map.size(); });
values_view.def("__iter__", values_view.def(
[](ValuesView &view) { "__iter__",
return make_value_iterator(view.map.begin(), view.map.end()); [](ValuesView &view) { return make_value_iterator(view.map.begin(), view.map.end()); },
},
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
); );
items_view.def("__len__", [](ItemsView &view) { return view.map.size(); }); items_view.def("__len__", [](ItemsView &view) { return view.map.size(); });
items_view.def("__iter__", items_view.def(
[](ItemsView &view) { "__iter__",
return make_iterator(view.map.begin(), view.map.end()); [](ItemsView &view) { return make_iterator(view.map.begin(), view.map.end()); },
},
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ 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.
In some cases, when you need to track instances of a C++ class not registered with pybind11, you 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: 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 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. inspection/testing in python) by using the functions with `print_` replaced with `track_` (e.g.
@ -65,17 +66,18 @@ inspection/testing in python) by using the functions with `print_` replaced with
*/ */
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <unordered_map>
#include <list> #include <list>
#include <typeindex>
#include <sstream> #include <sstream>
#include <typeindex>
#include <unordered_map>
class ConstructorStats { class ConstructorStats {
protected: protected:
std::unordered_map<void*, int> _instances; // Need a map rather than set because members can std::unordered_map<void *, int> _instances; // Need a map rather than set because members can
// shared address with parents // shared address with parents
std::list<std::string> _values; // Used to track values std::list<std::string> _values; // Used to track values
// (e.g. of value constructors) // (e.g. of value constructors)
public: public:
int default_constructions = 0; int default_constructions = 0;
int copy_constructions = 0; int copy_constructions = 0;
@ -98,9 +100,7 @@ public:
default_constructions++; default_constructions++;
} }
void created(void *inst) { void created(void *inst) { ++_instances[inst]; }
++_instances[inst];
}
void destroyed(void *inst) { void destroyed(void *inst) {
if (--_instances[inst] < 0) { if (--_instances[inst] < 0) {
@ -114,11 +114,12 @@ public:
// Force garbage collection to ensure any pending destructors are invoked: // Force garbage collection to ensure any pending destructors are invoked:
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
PyObject *globals = PyEval_GetGlobals(); PyObject *globals = PyEval_GetGlobals();
PyObject *result = PyRun_String( PyObject *result = PyRun_String("import gc\n"
"import gc\n" "for i in range(2):"
"for i in range(2):" " gc.collect()\n",
" gc.collect()\n", Py_file_input,
Py_file_input, globals, globals); globals,
globals);
if (result == nullptr) if (result == nullptr)
throw py::error_already_set(); throw py::error_already_set();
Py_DECREF(result); Py_DECREF(result);
@ -140,7 +141,8 @@ public:
void value() {} // Recursion terminator void value() {} // Recursion terminator
// Takes one or more values, converts them to strings, then stores them. // 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; std::ostringstream oss;
oss << v; oss << v;
_values.push_back(oss.str()); _values.push_back(oss.str());
@ -158,13 +160,14 @@ public:
} }
// Gets constructor stats from a C++ type index // 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; static std::unordered_map<std::type_index, ConstructorStats> all_cstats;
return all_cstats[type]; return all_cstats[type];
} }
// Gets constructor stats from a C++ type // Gets constructor stats from a C++ type
template <typename T> static ConstructorStats& get() { template <typename T>
static ConstructorStats &get() {
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
gc(); gc();
#endif #endif
@ -172,11 +175,12 @@ public:
} }
// Gets constructor stats from a Python class // 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(); auto &internals = py::detail::get_internals();
const std::type_index *t1 = nullptr, *t2 = nullptr; const std::type_index *t1 = nullptr, *t2 = nullptr;
try { 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) { for (auto &p : internals.registered_types_cpp) {
if (p.second == type_info) { if (p.second == type_info) {
if (t1) { if (t1) {
@ -186,18 +190,20 @@ public:
t1 = &p.first; t1 = &p.first;
} }
} }
} catch (const std::out_of_range &) {
} }
catch (const std::out_of_range&) {}
if (!t1) { if (!t1) {
throw std::runtime_error("Unknown class passed to ConstructorStats::get()"); throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
} }
auto &cs1 = get(*t1); auto &cs1 = get(*t1);
// If we have both a t1 and t2 match, one is probably the trampoline class; return whichever // If we have both a t1 and t2 match, one is probably the trampoline class; return
// has more constructions (typically one or the other will be 0) // whichever has more constructions (typically one or the other will be 0)
if (t2) { if (t2) {
auto &cs2 = get(*t2); auto &cs2 = get(*t2);
int cs1_total = cs1.default_constructions + cs1.copy_constructions + cs1.move_constructions + (int) cs1._values.size(); int cs1_total = cs1.default_constructions + cs1.copy_constructions
int cs2_total = cs2.default_constructions + cs2.copy_constructions + cs2.move_constructions + (int) cs2._values.size(); + 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) { if (cs2_total > cs1_total) {
return cs2; return cs2;
} }
@ -209,78 +215,108 @@ public:
// To track construction/destruction, you need to call these methods from the various // 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 // constructors/operators. The ones that take extra values record the given values in the
// constructor stats values for later inspection. // constructor stats values for later inspection.
template <class T> void track_copy_created(T *inst) { ConstructorStats::get<T>().copy_created(inst); } template <class T>
template <class T> void track_move_created(T *inst) { ConstructorStats::get<T>().move_created(inst); } void track_copy_created(T *inst) {
template <class T, typename... Values> void track_copy_assigned(T *, Values &&...values) { 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>(); auto &cst = ConstructorStats::get<T>();
cst.copy_assignments++; cst.copy_assignments++;
cst.value(std::forward<Values>(values)...); 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>(); auto &cst = ConstructorStats::get<T>();
cst.move_assignments++; cst.move_assignments++;
cst.value(std::forward<Values>(values)...); 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>(); auto &cst = ConstructorStats::get<T>();
cst.default_created(inst); cst.default_created(inst);
cst.value(std::forward<Values>(values)...); 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>(); auto &cst = ConstructorStats::get<T>();
cst.created(inst); cst.created(inst);
cst.value(std::forward<Values>(values)...); 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); 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)...); ConstructorStats::get<T>().value(std::forward<Values>(values)...);
} }
/// Don't cast pointers to Python, print them as strings /// Don't cast pointers to Python, print them as strings
inline const char *format_ptrs(const char *p) { return p; } inline const char *format_ptrs(const char *p) { return p; }
template <typename T> 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> 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> template <class T, typename... Output>
void print_constr_details(T *inst, const std::string &action, Output &&...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))...); format_ptrs(std::forward<Output>(output))...);
} }
// Verbose versions of the above: // 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...); print_constr_details(inst, "created via copy constructor", values...);
track_copy_created(inst); 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...); print_constr_details(inst, "created via move constructor", values...);
track_move_created(inst); 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...); print_constr_details(inst, "assigned via copy assignment", values...);
track_copy_assigned(inst, 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...); print_constr_details(inst, "assigned via move assignment", values...);
track_move_assigned(inst, 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...); print_constr_details(inst, "created via default constructor", values...);
track_default_created(inst, 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...); print_constr_details(inst, "created", values...);
track_created(inst, 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...); print_constr_details(inst, "destroyed", values...);
track_destroyed(inst); 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...); print_constr_details(inst, ":", values...);
track_values(inst, values...); track_values(inst, values...);
} }

37
tests/cross_module_gil_utils.cpp

@ -7,6 +7,7 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <cstdint> #include <cstdint>
// This file mimics a DSO that makes pybind11 calls but does not define a // This file mimics a DSO that makes pybind11 calls but does not define a
@ -25,34 +26,25 @@ void gil_acquire() { py::gil_scoped_acquire gil; }
constexpr char kModuleName[] = "cross_module_gil_utils"; constexpr char kModuleName[] = "cross_module_gil_utils";
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
struct PyModuleDef moduledef = { struct PyModuleDef moduledef
PyModuleDef_HEAD_INIT, = {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL};
kModuleName,
NULL,
0,
NULL,
NULL,
NULL,
NULL,
NULL
};
#else #else
PyMethodDef module_methods[] = { PyMethodDef module_methods[] = {{NULL, NULL, 0, NULL}};
{NULL, NULL, 0, NULL}
};
#endif #endif
} // namespace } // namespace
extern "C" PYBIND11_EXPORT extern "C" PYBIND11_EXPORT
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
PyObject* PyInit_cross_module_gil_utils() PyObject *
PyInit_cross_module_gil_utils()
#else #else
void initcross_module_gil_utils() void
initcross_module_gil_utils()
#endif #endif
{ {
PyObject* m = PyObject *m =
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
PyModule_Create(&moduledef); PyModule_Create(&moduledef);
#else #else
@ -60,11 +52,10 @@ void initcross_module_gil_utils()
#endif #endif
if (m != NULL) { if (m != NULL) {
static_assert( static_assert(sizeof(&gil_acquire) == sizeof(void *),
sizeof(&gil_acquire) == sizeof(void*), "Function pointer must have the same size as void*");
"Function pointer must have the same size as void*"); PyModule_AddObject(
PyModule_AddObject(m, "gil_acquire_funcaddr", m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire)));
PyLong_FromVoidPtr(reinterpret_cast<void*>(&gil_acquire)));
} }
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3

41
tests/local_bindings.h

@ -1,12 +1,13 @@
#pragma once #pragma once
#include <utility>
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <utility>
/// Simple class used to test py::local: /// Simple class used to test py::local:
template <int> class LocalBase { template <int>
class LocalBase {
public: public:
explicit LocalBase(int i) : i(i) { } explicit LocalBase(int i) : i(i) {}
int i = -1; int i = -1;
}; };
@ -35,12 +36,12 @@ using NonLocalVec2 = std::vector<NonLocal2>;
using NonLocalMap = std::unordered_map<std::string, NonLocalType>; using NonLocalMap = std::unordered_map<std::string, NonLocalType>;
using NonLocalMap2 = std::unordered_map<std::string, uint8_t>; using NonLocalMap2 = std::unordered_map<std::string, uint8_t>;
// Exception that will be caught via the module local translator. // Exception that will be caught via the module local translator.
class LocalException : public std::exception { class LocalException : public std::exception {
public: public:
explicit LocalException(const char * m) : message{m} {} explicit LocalException(const char *m) : message{m} {}
const char * what() const noexcept override {return message.c_str();} const char *what() const noexcept override { return message.c_str(); }
private: private:
std::string message = ""; std::string message = "";
}; };
@ -48,8 +49,9 @@ private:
// Exception that will be registered with register_local_exception_translator // Exception that will be registered with register_local_exception_translator
class LocalSimpleException : public std::exception { class LocalSimpleException : public std::exception {
public: public:
explicit LocalSimpleException(const char * m) : message{m} {} explicit LocalSimpleException(const char *m) : message{m} {}
const char * what() const noexcept override {return message.c_str();} const char *what() const noexcept override { return message.c_str(); }
private: private:
std::string message = ""; std::string message = "";
}; };
@ -58,17 +60,16 @@ PYBIND11_MAKE_OPAQUE(LocalVec);
PYBIND11_MAKE_OPAQUE(LocalVec2); PYBIND11_MAKE_OPAQUE(LocalVec2);
PYBIND11_MAKE_OPAQUE(LocalMap); PYBIND11_MAKE_OPAQUE(LocalMap);
PYBIND11_MAKE_OPAQUE(NonLocalVec); 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(NonLocalMap);
PYBIND11_MAKE_OPAQUE(NonLocalMap2); PYBIND11_MAKE_OPAQUE(NonLocalMap2);
// Simple bindings (used with the above): // Simple bindings (used with the above):
template <typename T, int Adjust = 0, typename... Args> template <typename T, int Adjust = 0, typename... Args>
py::class_<T> bind_local(Args && ...args) { py::class_<T> bind_local(Args &&...args) {
return py::class_<T>(std::forward<Args>(args)...) return py::class_<T>(std::forward<Args>(args)...).def(py::init<int>()).def("get", [](T &i) {
.def(py::init<int>()) return i.i + Adjust;
.def("get", [](T &i) { return i.i + Adjust; }); });
}; };
// Simulate a foreign library base class (to match the example in the docs): // Simulate a foreign library base class (to match the example in the docs):
@ -81,5 +82,11 @@ public:
}; };
} // namespace pets } // namespace pets
struct MixGL { int i; explicit MixGL(int i) : i{i} {} }; struct MixGL {
struct MixGL2 { int i; explicit MixGL2(int i) : i{i} {} }; 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 @@
#if !defined(__OBJECT_H) #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 /// Reference counted object base class
class Object { class Object {
@ -35,13 +36,15 @@ public:
} }
virtual std::string toString() const = 0; virtual std::string toString() const = 0;
protected: protected:
/** \brief Virtual protected deconstructor. /** \brief Virtual protected deconstructor.
* (Will only be called by \ref ref) * (Will only be called by \ref ref)
*/ */
virtual ~Object() { print_destroyed(this); } virtual ~Object() { print_destroyed(this); }
private: 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 // Tag class used to track constructions of ref objects. When we track constructors, below, we
@ -60,10 +63,14 @@ class ref_tag {};
* *
* \ingroup libcore * \ingroup libcore
*/ */
template <typename T> class ref { template <typename T>
class ref {
public: public:
/// Create a nullptr reference /// 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 /// Construct a reference from a pointer
explicit ref(T *ptr) : m_ptr(ptr) { explicit ref(T *ptr) : m_ptr(ptr) {
@ -71,8 +78,8 @@ public:
((Object *) m_ptr)->incRef(); ((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 /// Copy constructor
@ -81,14 +88,16 @@ public:
((Object *) m_ptr)->incRef(); ((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 /// Move constructor
ref(ref &&r) noexcept : m_ptr(r.m_ptr) { ref(ref &&r) noexcept : m_ptr(r.m_ptr) {
r.m_ptr = nullptr; 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 /// Destroy this reference
@ -97,12 +106,14 @@ public:
((Object *) m_ptr)->decRef(); ((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 /// Move another reference into the current one
ref &operator=(ref &&r) noexcept { 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) { if (*this == r) {
return *this; return *this;
@ -116,7 +127,7 @@ public:
} }
/// Overwrite this reference with another reference /// Overwrite this reference with another reference
ref& operator=(const ref& r) { ref &operator=(const ref &r) {
if (this == &r) { if (this == &r) {
return *this; return *this;
} }
@ -137,8 +148,9 @@ public:
} }
/// Overwrite this reference with a pointer to another object /// Overwrite this reference with a pointer to another object
ref& operator=(T *ptr) { ref &operator=(T *ptr) {
print_values(this, "assigned pointer"); track_values((ref_tag*) this, "assigned pointer"); print_values(this, "assigned pointer");
track_values((ref_tag *) this, "assigned pointer");
if (m_ptr == ptr) { if (m_ptr == ptr) {
return *this; return *this;
@ -160,31 +172,32 @@ public:
bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; } bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; }
/// Compare this reference with a pointer /// 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 /// 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 /// Access the object referenced by this reference
T* operator->() { return m_ptr; } T *operator->() { return m_ptr; }
/// Access the object referenced by this reference /// 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 /// 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 /// 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 /// 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 /// 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 /// Return a pointer to the referenced object
const T* get_ptr() const { return m_ptr; } const T *get_ptr() const { return m_ptr; }
private: private:
T *m_ptr; T *m_ptr;
}; };

86
tests/pybind11_cross_module_tests.cpp

@ -7,12 +7,12 @@
BSD-style license that can be found in the LICENSE file. 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 "local_bindings.h"
#include "pybind11_tests.h"
#include "test_exceptions.h" #include "test_exceptions.h"
#include <pybind11/stl_bind.h>
#include <numeric> #include <numeric>
#include <utility> #include <utility>
@ -30,39 +30,45 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
// test_exceptions.py // test_exceptions.py
py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException"); 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_runtime_error", []() {
m.def("raise_value_error", []() { PyErr_SetString(PyExc_ValueError, "My value error"); throw py::error_already_set(); }); 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_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_pybind_type_error", []() { throw py::type_error("pybind11 type error"); });
m.def("throw_stop_iteration", []() { throw py::stop_iteration(); }); m.def("throw_stop_iteration", []() { throw py::stop_iteration(); });
m.def("throw_local_error", []() { throw LocalException("just local"); }); m.def("throw_local_error", []() { throw LocalException("just local"); });
m.def("throw_local_simple_error", []() { throw LocalSimpleException("external mod"); }); m.def("throw_local_simple_error", []() { throw LocalSimpleException("external mod"); });
py::register_exception_translator([](std::exception_ptr p) { py::register_exception_translator([](std::exception_ptr p) {
try { try {
if (p) { if (p) {
std::rethrow_exception(p); std::rethrow_exception(p);
} }
} catch (const shared_exception &e) { } catch (const shared_exception &e) {
PyErr_SetString(PyExc_KeyError, e.what()); PyErr_SetString(PyExc_KeyError, e.what());
} }
}); });
// translate the local exception into a key error but only in this module // translate the local exception into a key error but only in this module
py::register_local_exception_translator([](std::exception_ptr p) { py::register_local_exception_translator([](std::exception_ptr p) {
try { try {
if (p) { if (p) {
std::rethrow_exception(p); std::rethrow_exception(p);
} }
} catch (const LocalException &e) { } catch (const LocalException &e) {
PyErr_SetString(PyExc_KeyError, e.what()); PyErr_SetString(PyExc_KeyError, e.what());
} }
}); });
// test_local_bindings.py // test_local_bindings.py
// Local to both: // Local to both:
bind_local<LocalType, 1>(m, "LocalType", py::module_local()) bind_local<LocalType, 1>(m, "LocalType", py::module_local()).def("get2", [](LocalType &t) {
.def("get2", [](LocalType &t) { return t.i + 2; }) return t.i + 2;
; });
// Can only be called with our python type: // Can only be called with our python type:
m.def("local_value", [](LocalType &l) { return l.i; }); m.def("local_value", [](LocalType &l) { return l.i; });
@ -70,9 +76,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
// test_nonlocal_failure // test_nonlocal_failure
// This registration will fail (global registration when LocalFail is already registered // This registration will fail (global registration when LocalFail is already registered
// globally in the main test module): // globally in the main test module):
m.def("register_nonlocal", [m]() { m.def("register_nonlocal", [m]() { bind_local<NonLocalType, 0>(m, "NonLocalType"); });
bind_local<NonLocalType, 0>(m, "NonLocalType");
});
// test_stl_bind_local // test_stl_bind_local
// stl_bind.h binders defaults to py::module_local if the types are local or converting: // stl_bind.h binders defaults to py::module_local if the types are local or converting:
@ -82,27 +86,21 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
// test_stl_bind_global // test_stl_bind_global
// and global if the type (or one of the types, for the map) is global (so these will fail, // 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): // assuming pybind11_tests is already loaded):
m.def("register_nonlocal_vec", [m]() { m.def("register_nonlocal_vec", [m]() { py::bind_vector<NonLocalVec>(m, "NonLocalVec"); });
py::bind_vector<NonLocalVec>(m, "NonLocalVec"); m.def("register_nonlocal_map", [m]() { py::bind_map<NonLocalMap>(m, "NonLocalMap"); });
});
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 // The default can, however, be overridden to global using `py::module_local()` or
// `py::module_local(false)`. // `py::module_local(false)`.
// Explicitly made local: // Explicitly made local:
py::bind_vector<NonLocalVec2>(m, "NonLocalVec2", py::module_local()); py::bind_vector<NonLocalVec2>(m, "NonLocalVec2", py::module_local());
// Explicitly made global (and so will fail to bind): // Explicitly made global (and so will fail to bind):
m.def("register_nonlocal_map2", [m]() { m.def("register_nonlocal_map2",
py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false)); [m]() { py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false)); });
});
// test_mixed_local_global // test_mixed_local_global
// We try this both with the global type registered first and vice versa (the order shouldn't // We try this both with the global type registered first and vice versa (the order shouldn't
// matter). // matter).
m.def("register_mixed_global_local", [m]() { m.def("register_mixed_global_local",
bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local()); [m]() { bind_local<MixedGlobalLocal, 200>(m, "MixedGlobalLocal", py::module_local()); });
});
m.def("register_mixed_local_global", [m]() { m.def("register_mixed_local_global", [m]() {
bind_local<MixedLocalGlobal, 2000>(m, "MixedLocalGlobal", py::module_local(false)); bind_local<MixedLocalGlobal, 2000>(m, "MixedLocalGlobal", py::module_local(false));
}); });
@ -110,14 +108,14 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); }); m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); });
// test_internal_locals_differ // 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 // test_stl_caster_vs_stl_bind
py::bind_vector<std::vector<int>>(m, "VectorInt"); py::bind_vector<std::vector<int>>(m, "VectorInt");
m.def("load_vector_via_binding", [](std::vector<int> &v) { m.def("load_vector_via_binding",
return std::accumulate(v.begin(), v.end(), 0); [](std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); });
});
// test_cross_module_calls // test_cross_module_calls
m.def("return_self", [](LocalVec *v) { return v; }); m.def("return_self", [](LocalVec *v) { return v; });
@ -127,11 +125,9 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
public: public:
explicit Dog(std::string name) : Pet(std::move(name)) {} explicit Dog(std::string name) : Pet(std::move(name)) {}
}; };
py::class_<pets::Pet>(m, "Pet", py::module_local()) py::class_<pets::Pet>(m, "Pet", py::module_local()).def("name", &pets::Pet::name);
.def("name", &pets::Pet::name);
// Binding for local extending class: // Binding for local extending class:
py::class_<Dog, pets::Pet>(m, "Dog") py::class_<Dog, pets::Pet>(m, "Dog").def(py::init<std::string>());
.def(py::init<std::string>());
m.def("pet_name", [](pets::Pet &p) { return p.name(); }); m.def("pet_name", [](pets::Pet &p) { return p.name(); });
py::class_<MixGL>(m, "MixGL", py::module_local()).def(py::init<int>()); py::class_<MixGL>(m, "MixGL", py::module_local()).def(py::init<int>());

20
tests/pybind11_tests.cpp

@ -8,6 +8,7 @@
*/ */
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
#include <functional> #include <functional>
@ -31,9 +32,7 @@ std::list<std::function<void(py::module_ &)>> &initializers() {
return inits; return inits;
} }
test_initializer::test_initializer(Initializer init) { test_initializer::test_initializer(Initializer init) { initializers().emplace_back(init); }
initializers().emplace_back(init);
}
test_initializer::test_initializer(const char *submodule_name, Initializer init) { test_initializer::test_initializer(const char *submodule_name, Initializer init) {
initializers().emplace_back([=](py::module_ &parent) { initializers().emplace_back([=](py::module_ &parent) {
@ -51,15 +50,16 @@ void bind_ConstructorStats(py::module_ &m) {
.def_readwrite("move_assignments", &ConstructorStats::move_assignments) .def_readwrite("move_assignments", &ConstructorStats::move_assignments)
.def_readwrite("copy_constructions", &ConstructorStats::copy_constructions) .def_readwrite("copy_constructions", &ConstructorStats::copy_constructions)
.def_readwrite("move_constructions", &ConstructorStats::move_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 // Not exactly ConstructorStats, but related: expose the internal pybind number of
// to allow instance cleanup checks (invokes a GC first) // registered instances to allow instance cleanup checks (invokes a GC first)
.def_static("detail_reg_inst", []() { .def_static("detail_reg_inst", []() {
ConstructorStats::gc(); ConstructorStats::gc();
return py::detail::get_internals().registered_instances.size(); return py::detail::get_internals().registered_instances.size();
}) });
;
} }
PYBIND11_MODULE(pybind11_tests, m) { 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("get_value", &UserType::value, "Get value using a method")
.def("set_value", &UserType::set, "Set 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_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") py::class_<IncType, UserType>(m, "IncType")
.def(py::init<>()) .def(py::init<>())
.def(py::init<int>()) .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()) { for (const auto &initializer : initializers()) {
initializer(m); initializer(m);

29
tests/pybind11_tests.h

@ -1,11 +1,12 @@
#pragma once #pragma once
#include <pybind11/pybind11.h>
#include <pybind11/eval.h> #include <pybind11/eval.h>
#include <pybind11/pybind11.h>
#if defined(_MSC_VER) && _MSC_VER < 1910 #if defined(_MSC_VER) && _MSC_VER < 1910
// We get some really long type names here which causes MSVC 2015 to emit warnings // 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 #endif
namespace py = pybind11; namespace py = pybind11;
@ -25,13 +26,13 @@ public:
void test_submodule_##name(py::module_ &(variable)) void test_submodule_##name(py::module_ &(variable))
/// Dummy type which is not exported anywhere -- something to trigger a conversion error /// 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 /// A user-defined type which is exported and can be used by any test
class UserType { class UserType {
public: public:
UserType() = default; UserType() = default;
explicit UserType(int i) : i(i) { } explicit UserType(int i) : i(i) {}
int value() const { return i; } int value() const { return i; }
void set(int set) { i = set; } void set(int set) { i = set; }
@ -45,7 +46,7 @@ class IncType : public UserType {
public: public:
using UserType::UserType; using UserType::UserType;
IncType() = default; IncType() = default;
IncType(const IncType &other) : IncType(other.value() + 1) { } IncType(const IncType &other) : IncType(other.value() + 1) {}
IncType(IncType &&) = delete; IncType(IncType &&) = delete;
IncType &operator=(const IncType &) = delete; IncType &operator=(const IncType &) = delete;
IncType &operator=(IncType &&) = delete; IncType &operator=(IncType &&) = delete;
@ -57,16 +58,21 @@ union IntFloat {
float f; float f;
}; };
/// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast context. /// Custom cast-only type that casts to a string "rvalue" or "lvalue" depending on the cast
/// Used to test recursive casters (e.g. std::tuple, stl containers). /// context. Used to test recursive casters (e.g. std::tuple, stl containers).
struct RValueCaster {}; struct RValueCaster {};
PYBIND11_NAMESPACE_BEGIN(pybind11) PYBIND11_NAMESPACE_BEGIN(pybind11)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template<> class type_caster<RValueCaster> { template <>
class type_caster<RValueCaster> {
public: public:
PYBIND11_TYPE_CASTER(RValueCaster, const_name("RValueCaster")); PYBIND11_TYPE_CASTER(RValueCaster, const_name("RValueCaster"));
static handle cast(RValueCaster &&, return_value_policy, handle) { return py::str("rvalue").release(); } static handle cast(RValueCaster &&, return_value_policy, handle) {
static handle cast(const RValueCaster &, return_value_policy, handle) { return py::str("lvalue").release(); } 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(detail)
PYBIND11_NAMESPACE_END(pybind11) PYBIND11_NAMESPACE_END(pybind11)
@ -80,5 +86,6 @@ void ignoreOldStyleInitWarnings(F &&body) {
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.filterwarnings("ignore", message=message, category=FutureWarning) warnings.filterwarnings("ignore", message=message, category=FutureWarning)
body() 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 @@
TEST_SUBMODULE(async_module, m) { TEST_SUBMODULE(async_module, m) {
struct DoesNotSupportAsync {}; struct DoesNotSupportAsync {};
py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync") py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync").def(py::init<>());
.def(py::init<>());
struct SupportsAsync {}; struct SupportsAsync {};
py::class_<SupportsAsync>(m, "SupportsAsync") py::class_<SupportsAsync>(m, "SupportsAsync")
.def(py::init<>()) .def(py::init<>())
.def("__await__", [](const SupportsAsync& self) -> py::object { .def("__await__", [](const SupportsAsync &self) -> py::object {
static_cast<void>(self); static_cast<void>(self);
py::object loop = py::module_::import("asyncio.events").attr("get_event_loop")(); py::object loop = py::module_::import("asyncio.events").attr("get_event_loop")();
py::object f = loop.attr("create_future")(); py::object f = loop.attr("create_future")();

67
tests/test_buffers.cpp

@ -7,10 +7,11 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
TEST_SUBMODULE(buffers, m) { TEST_SUBMODULE(buffers, m) {
// test_from_python / test_to_python: // test_from_python / test_to_python:
class Matrix { class Matrix {
@ -23,7 +24,8 @@ TEST_SUBMODULE(buffers, m) {
} }
Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) { 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) // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
m_data = new float[(size_t) (m_rows * m_cols)]; m_data = new float[(size_t) (m_rows * m_cols)];
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols)); memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
@ -37,7 +39,8 @@ TEST_SUBMODULE(buffers, m) {
} }
~Matrix() { ~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; delete[] m_data;
} }
@ -56,27 +59,33 @@ TEST_SUBMODULE(buffers, m) {
} }
Matrix &operator=(Matrix &&s) noexcept { 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) { if (&s != this) {
delete[] m_data; delete[] m_data;
m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data; m_rows = s.m_rows;
s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr; m_cols = s.m_cols;
m_data = s.m_data;
s.m_rows = 0;
s.m_cols = 0;
s.m_data = nullptr;
} }
return *this; return *this;
} }
float operator()(py::ssize_t i, py::ssize_t j) const { 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) { 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; } float *data() { return m_data; }
py::ssize_t rows() const { return m_rows; } py::ssize_t rows() const { return m_rows; }
py::ssize_t cols() const { return m_cols; } py::ssize_t cols() const { return m_cols; }
private: private:
py::ssize_t m_rows; py::ssize_t m_rows;
py::ssize_t m_cols; py::ssize_t m_cols;
@ -117,11 +126,10 @@ TEST_SUBMODULE(buffers, m) {
/// Provide buffer access /// Provide buffer access
.def_buffer([](Matrix &m) -> py::buffer_info { .def_buffer([](Matrix &m) -> py::buffer_info {
return py::buffer_info( return py::buffer_info(
m.data(), /* Pointer to buffer */ m.data(), /* Pointer to buffer */
{ m.rows(), m.cols() }, /* Buffer dimensions */ {m.rows(), m.cols()}, /* Buffer dimensions */
{ sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */ {sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
sizeof(float) } sizeof(float)});
);
}); });
// test_inherited_protocol // test_inherited_protocol
@ -130,9 +138,7 @@ TEST_SUBMODULE(buffers, m) {
explicit SquareMatrix(py::ssize_t n) : Matrix(n, n) {} explicit SquareMatrix(py::ssize_t n) : Matrix(n, n) {}
}; };
// Derived classes inherit the buffer protocol and the buffer access function // Derived classes inherit the buffer protocol and the buffer access function
py::class_<SquareMatrix, Matrix>(m, "SquareMatrix") py::class_<SquareMatrix, Matrix>(m, "SquareMatrix").def(py::init<py::ssize_t>());
.def(py::init<py::ssize_t>());
// test_pointer_to_member_fn // test_pointer_to_member_fn
// Tests that passing a pointer to member to the base class works in // Tests that passing a pointer to member to the base class works in
@ -141,8 +147,8 @@ TEST_SUBMODULE(buffers, m) {
int32_t value = 0; int32_t value = 0;
py::buffer_info get_buffer_info() { py::buffer_info get_buffer_info() {
return py::buffer_info(&value, sizeof(value), return py::buffer_info(
py::format_descriptor<int32_t>::format(), 1); &value, sizeof(value), py::format_descriptor<int32_t>::format(), 1);
} }
}; };
py::class_<Buffer>(m, "Buffer", py::buffer_protocol()) py::class_<Buffer>(m, "Buffer", py::buffer_protocol())
@ -150,7 +156,6 @@ TEST_SUBMODULE(buffers, m) {
.def_readwrite("value", &Buffer::value) .def_readwrite("value", &Buffer::value)
.def_buffer(&Buffer::get_buffer_info); .def_buffer(&Buffer::get_buffer_info);
class ConstBuffer { class ConstBuffer {
std::unique_ptr<int32_t> value; std::unique_ptr<int32_t> value;
@ -159,8 +164,8 @@ TEST_SUBMODULE(buffers, m) {
void set_value(int32_t v) { *value = v; } void set_value(int32_t v) { *value = v; }
py::buffer_info get_buffer_info() const { py::buffer_info get_buffer_info() const {
return py::buffer_info(value.get(), sizeof(*value), return py::buffer_info(
py::format_descriptor<int32_t>::format(), 1); value.get(), sizeof(*value), py::format_descriptor<int32_t>::format(), 1);
} }
ConstBuffer() : value(new int32_t{0}) {} ConstBuffer() : value(new int32_t{0}) {}
@ -170,7 +175,7 @@ TEST_SUBMODULE(buffers, m) {
.def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value) .def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value)
.def_buffer(&ConstBuffer::get_buffer_info); .def_buffer(&ConstBuffer::get_buffer_info);
struct DerivedBuffer : public Buffer { }; struct DerivedBuffer : public Buffer {};
py::class_<DerivedBuffer>(m, "DerivedBuffer", py::buffer_protocol()) py::class_<DerivedBuffer>(m, "DerivedBuffer", py::buffer_protocol())
.def(py::init<>()) .def(py::init<>())
.def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value) .def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
@ -180,9 +185,7 @@ TEST_SUBMODULE(buffers, m) {
const uint8_t value = 0; const uint8_t value = 0;
explicit BufferReadOnly(uint8_t value) : value(value) {} explicit BufferReadOnly(uint8_t value) : value(value) {}
py::buffer_info get_buffer_info() { py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1); }
return py::buffer_info(&value, 1);
}
}; };
py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol()) py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol())
.def(py::init<uint8_t>()) .def(py::init<uint8_t>())
@ -192,9 +195,7 @@ TEST_SUBMODULE(buffers, m) {
uint8_t value = 0; uint8_t value = 0;
bool readonly = false; bool readonly = false;
py::buffer_info get_buffer_info() { py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1, readonly); }
return py::buffer_info(&value, 1, readonly);
}
}; };
py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol()) py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol())
.def(py::init<>()) .def(py::init<>())
@ -213,9 +214,11 @@ TEST_SUBMODULE(buffers, m) {
.def_readonly("strides", &py::buffer_info::strides) .def_readonly("strides", &py::buffer_info::strides)
.def_readonly("readonly", &py::buffer_info::readonly) .def_readonly("readonly", &py::buffer_info::readonly)
.def("__repr__", [](py::handle self) { .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(); }); m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); });
} }

298
tests/test_builtin_casters.cpp

@ -7,64 +7,67 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/complex.h> #include <pybind11/complex.h>
#include "pybind11_tests.h"
struct ConstRefCasted { struct ConstRefCasted {
int tag; int tag;
}; };
PYBIND11_NAMESPACE_BEGIN(pybind11) PYBIND11_NAMESPACE_BEGIN(pybind11)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <> template <>
class type_caster<ConstRefCasted> { class type_caster<ConstRefCasted> {
public: public:
static constexpr auto name = const_name<ConstRefCasted>(); static constexpr auto name = const_name<ConstRefCasted>();
// Input is unimportant, a new value will always be constructed based on the // Input is unimportant, a new value will always be constructed based on the
// cast operator. // cast operator.
bool load(handle, bool) { return true; } bool load(handle, bool) { return true; }
explicit operator ConstRefCasted &&() { explicit operator ConstRefCasted &&() {
value = {1}; value = {1};
// NOLINTNEXTLINE(performance-move-const-arg) // NOLINTNEXTLINE(performance-move-const-arg)
return std::move(value); return std::move(value);
} }
explicit operator ConstRefCasted &() { explicit operator ConstRefCasted &() {
value = {2}; value = {2};
return value; return value;
} }
explicit operator ConstRefCasted *() { explicit operator ConstRefCasted *() {
value = {3}; value = {3};
return &value; return &value;
} }
explicit operator const ConstRefCasted &() { explicit operator const ConstRefCasted &() {
value = {4}; value = {4};
return value; return value;
} }
explicit operator const ConstRefCasted *() { explicit operator const ConstRefCasted *() {
value = {5}; value = {5};
return &value; return &value;
} }
// custom cast_op to explicitly propagate types to the conversion operators. // custom cast_op to explicitly propagate types to the conversion operators.
template <typename T_> template <typename T_>
using cast_op_type = using cast_op_type =
/// const /// const
conditional_t< conditional_t<
std::is_same<remove_reference_t<T_>, const ConstRefCasted*>::value, const ConstRefCasted*, std::is_same<remove_reference_t<T_>, const ConstRefCasted *>::value,
conditional_t< const ConstRefCasted *,
std::is_same<T_, const ConstRefCasted&>::value, const ConstRefCasted&, conditional_t<
/// non-const std::is_same<T_, const ConstRefCasted &>::value,
conditional_t< const ConstRefCasted &,
std::is_same<remove_reference_t<T_>, ConstRefCasted*>::value, ConstRefCasted*, /// non-const
conditional_t< conditional_t<std::is_same<remove_reference_t<T_>, ConstRefCasted *>::value,
std::is_same<T_, ConstRefCasted&>::value, ConstRefCasted&, ConstRefCasted *,
/* else */ConstRefCasted&&>>>>; conditional_t<std::is_same<T_, ConstRefCasted &>::value,
ConstRefCasted &,
private: /* else */ ConstRefCasted &&>>>>;
ConstRefCasted value = {0};
private:
ConstRefCasted value = {0};
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(pybind11) PYBIND11_NAMESPACE_END(pybind11)
@ -74,25 +77,43 @@ TEST_SUBMODULE(builtin_casters, m) {
m.def("string_roundtrip", [](const char *s) { return s; }); m.def("string_roundtrip", [](const char *s) { return s; });
// test_unicode_conversion // test_unicode_conversion
// Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte // Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null
char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, mathbfA32 = 0x1d400 /*𝐀*/; // byte
char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; 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; std::wstring wstr;
wstr.push_back(0x61); // a wstr.push_back(0x61); // a
wstr.push_back(0x2e18); // ⸘ wstr.push_back(0x2e18); // ⸘
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16 if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32 wstr.push_back(mathbfA16_1);
wstr.push_back(mathbfA16_2);
} // 𝐀, utf16
else {
wstr.push_back((wchar_t) mathbfA32);
} // 𝐀, utf32
wstr.push_back(0x7a); // z 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_utf8_string", []() {
m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // b‽🎂𝐀z return std::string((const char *) u8"Say utf8\u203d \U0001f382 \U0001d400");
m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // a𝐀🎂‽z }); // 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("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z
m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); }); m.def("bad_utf8_string", []() {
m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); }); return std::string("abc\xd0"
"def");
});
m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); });
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger
m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); }); // UnicodeDecodeError
m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); });
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
m.def("bad_wchar_string", [=]() { m.def("bad_wchar_string", [=]() {
return std::wstring({wchar_t(0x61), wchar_t(0xd800)}); return std::wstring({wchar_t(0x61), wchar_t(0xd800)});
@ -120,8 +141,13 @@ TEST_SUBMODULE(builtin_casters, m) {
#ifdef PYBIND11_HAS_U8STRING #ifdef PYBIND11_HAS_U8STRING
m.attr("has_u8string") = true; m.attr("has_u8string") = true;
m.def("good_utf8_u8string", []() { return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀 m.def("good_utf8_u8string", []() {
m.def("bad_utf8_u8string", []() { return std::u8string((const char8_t*)"abc\xd0" "def"); }); 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'; }); m.def("u8_char8_Z", []() -> char8_t { return u8'Z'; });
@ -133,46 +159,63 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_string_view // test_string_view
#ifdef PYBIND11_HAS_STRING_VIEW #ifdef PYBIND11_HAS_STRING_VIEW
m.attr("has_string_view") = true; 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_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_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) { for (auto c : s) {
l.append((std::uint8_t) c); l.append((std::uint8_t) c);
} }
return l; }); return l;
m.def("string_view16_chars", [](std::u16string_view s) { py::list l; });
m.def("string_view16_chars", [](std::u16string_view s) {
py::list l;
for (auto c : s) { for (auto c : s) {
l.append((int) c); l.append((int) c);
} }
return l; }); return l;
m.def("string_view32_chars", [](std::u32string_view s) { py::list l; });
m.def("string_view32_chars", [](std::u32string_view s) {
py::list l;
for (auto c : s) { for (auto c : s) {
l.append((int) c); l.append((int) c);
} }
return l; }); 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_view_return",
m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); }); []() { 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 // The inner lambdas here are to also test implicit conversion
using namespace std::literals; using namespace std::literals;
m.def("string_view_bytes", []() { return [](py::bytes b) { return b; }("abc \x80\x80 def"sv); }); m.def("string_view_bytes",
m.def("string_view_str", []() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); }); []() { return [](py::bytes b) { return b; }("abc \x80\x80 def"sv); });
m.def("string_view_from_bytes", [](const py::bytes &b) { return [](std::string_view s) { return s; }(b); }); m.def("string_view_str",
#if PY_MAJOR_VERSION >= 3 []() { 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", []() { m.def("string_view_memoryview", []() {
static constexpr auto val = "Have some \360\237\216\202"sv; static constexpr auto val = "Have some \360\237\216\202"sv;
return py::memoryview::from_memory(val); return py::memoryview::from_memory(val);
}); });
#endif # endif
# ifdef PYBIND11_HAS_U8STRING # ifdef PYBIND11_HAS_U8STRING
m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); }); 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_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_return", []() { return std::u8string_view(u8"utf8 secret \U0001f382"); });
m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; }); m.def("string_view8_str", []() { return py::str{std::u8string_view{u8"abc ‽ def"}}; });
# endif # endif
struct TypeWithBothOperatorStringAndStringView { struct TypeWithBothOperatorStringAndStringView {
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
@ -194,7 +237,8 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_int_convert // test_int_convert
m.def("int_passthrough", [](int arg) { return arg; }); 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 // test_tuple
m.def( m.def(
@ -203,19 +247,27 @@ TEST_SUBMODULE(builtin_casters, m) {
return std::make_pair(input.second, input.first); return std::make_pair(input.second, input.first);
}, },
"Return a pair in reversed order"); "Return a pair in reversed order");
m.def("tuple_passthrough", [](std::tuple<bool, std::string, int> input) { m.def(
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input)); "tuple_passthrough",
}, "Return a triple in reversed order"); [](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<>(); }); m.def("empty_tuple", []() { return std::tuple<>(); });
static std::pair<RValueCaster, RValueCaster> lvpair; static std::pair<RValueCaster, RValueCaster> lvpair;
static std::tuple<RValueCaster, RValueCaster, RValueCaster> lvtuple; 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("rvalue_pair", []() { return std::make_pair(RValueCaster{}, RValueCaster{}); });
m.def("lvalue_pair", []() -> const decltype(lvpair) & { return lvpair; }); 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("lvalue_tuple", []() -> const decltype(lvtuple) & { return lvtuple; });
m.def("rvalue_nested", []() { 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; }); m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; });
static std::pair<int, std::string> int_string_pair{2, "items"}; static std::pair<int, std::string> int_string_pair{2, "items"};
@ -223,11 +275,11 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_builtins_cast_return_none // test_builtins_cast_return_none
m.def("return_none_string", []() -> std::string * { return nullptr; }); m.def("return_none_string", []() -> std::string * { return nullptr; });
m.def("return_none_char", []() -> const char * { return nullptr; }); m.def("return_none_char", []() -> const char * { return nullptr; });
m.def("return_none_bool", []() -> bool * { return nullptr; }); m.def("return_none_bool", []() -> bool * { return nullptr; });
m.def("return_none_int", []() -> int * { return nullptr; }); m.def("return_none_int", []() -> int * { return nullptr; });
m.def("return_none_float", []() -> float * { 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_pair", []() -> std::pair<int, int> * { return nullptr; });
// test_none_deferred // test_none_deferred
m.def("defer_none_cstring", [](char *) { return false; }); m.def("defer_none_cstring", [](char *) { return false; });
@ -245,7 +297,8 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_bool_caster // test_bool_caster
m.def("bool_passthrough", [](bool arg) { return arg; }); 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 // TODO: This should be disabled and fixed in future Intel compilers
#if !defined(__INTEL_COMPILER) #if !defined(__INTEL_COMPILER)
@ -253,13 +306,15 @@ TEST_SUBMODULE(builtin_casters, m) {
// When compiled with the Intel compiler, this results in segmentation faults when importing // 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 // the module. Tested with icc (ICC) 2021.1 Beta 20200827, this should be tested again when
// a newer version of icc is available. // 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 #endif
// test_reference_wrapper // test_reference_wrapper
m.def("refwrap_builtin", [](std::reference_wrapper<int> p) { return 10 * p.get(); }); 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", [](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> { m.def("refwrap_lvalue", []() -> std::reference_wrapper<UserType> {
static UserType x(1); static UserType x(1);
@ -272,17 +327,20 @@ TEST_SUBMODULE(builtin_casters, m) {
// Not currently supported (std::pair caster has return-by-value cast operator); // Not currently supported (std::pair caster has return-by-value cast operator);
// triggers static_assert failure. // 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) { m.def(
static IncType x1(1), x2(2); "refwrap_list",
py::list l; [](bool copy) {
for (const auto &f : {std::ref(x1), std::ref(x2)}) { static IncType x1(1), x2(2);
l.append(py::cast(f, copy ? py::return_value_policy::copy py::list l;
: py::return_value_policy::reference)); for (const auto &f : {std::ref(x1), std::ref(x2)}) {
} l.append(py::cast(
return l; f, copy ? py::return_value_policy::copy : py::return_value_policy::reference));
}, "copy"_a); }
return l;
},
"copy"_a);
m.def("refwrap_iiw", [](const IncType &w) { return w.value(); }); m.def("refwrap_iiw", [](const IncType &w) { return w.value(); });
m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) { m.def("refwrap_call_iiw", [](IncType &w, const py::function &f) {
@ -299,12 +357,13 @@ TEST_SUBMODULE(builtin_casters, m) {
// test_complex // test_complex
m.def("complex_cast", [](float x) { return "{}"_s.format(x); }); 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) // test int vs. long (Python 2)
m.def("int_cast", []() {return (int) 42;}); m.def("int_cast", []() { return (int) 42; });
m.def("long_cast", []() {return (long) 42;}); m.def("long_cast", []() { return (long) 42; });
m.def("longlong_cast", []() {return ULLONG_MAX;}); m.def("longlong_cast", []() { return ULLONG_MAX; });
/// test void* cast operator /// test void* cast operator
m.def("test_void_caster", []() -> bool { m.def("test_void_caster", []() -> bool {
@ -315,11 +374,12 @@ TEST_SUBMODULE(builtin_casters, m) {
// Tests const/non-const propagation in cast_op. // Tests const/non-const propagation in cast_op.
m.def("takes", [](ConstRefCasted x) { return x.tag; }); m.def("takes", [](ConstRefCasted x) { return x.tag; });
m.def("takes_move", [](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_ptr", [](ConstRefCasted *x) { return x->tag; });
m.def("takes_ref", [](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_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_ptr", [](const ConstRefCasted *x) { return x->tag; });
m.def("takes_const_ref", [](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_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) {
Child(Child &&) = default; Child(Child &&) = default;
~Child() { py::print("Releasing child."); } ~Child() { py::print("Releasing child."); }
}; };
py::class_<Child>(m, "Child") py::class_<Child>(m, "Child").def(py::init<>());
.def(py::init<>());
class Parent { class Parent {
public: public:
Parent() { py::print("Allocating parent."); } Parent() { py::print("Allocating parent."); }
Parent(const Parent& parent) = default; Parent(const Parent &parent) = default;
~Parent() { py::print("Releasing parent."); } ~Parent() { py::print("Releasing parent."); }
void addChild(Child *) { } void addChild(Child *) {}
Child *returnChild() { return new Child(); } Child *returnChild() { return new Child(); }
Child *returnNullChild() { return nullptr; } Child *returnNullChild() { return nullptr; }
static Child *staticFunction(Parent*) { return new Child(); } static Child *staticFunction(Parent *) { return new Child(); }
}; };
py::class_<Parent>(m, "Parent") py::class_<Parent>(m, "Parent")
.def(py::init<>()) .def(py::init<>())
@ -62,11 +61,12 @@ TEST_SUBMODULE(call_policies, m) {
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>()) .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>()) .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>()) .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>())
.def_static( .def_static("staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
"staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
m.def("free_function", [](Parent*, Child*) {}, py::keep_alive<1, 2>()); m.def(
m.def("invalid_arg_index", []{}, py::keep_alive<0, 1>()); "free_function", [](Parent *, Child *) {}, py::keep_alive<1, 2>());
m.def(
"invalid_arg_index", [] {}, py::keep_alive<0, 1>());
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
// test_alive_gc // test_alive_gc
@ -74,21 +74,28 @@ TEST_SUBMODULE(call_policies, m) {
public: public:
using Parent::Parent; using Parent::Parent;
}; };
py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()) py::class_<ParentGC, Parent>(m, "ParentGC", py::dynamic_attr()).def(py::init<>());
.def(py::init<>());
#endif #endif
// test_call_guard // test_call_guard
m.def("unguarded_call", &CustomGuard::report_status); m.def("unguarded_call", &CustomGuard::report_status);
m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>()); m.def("guarded_call", &CustomGuard::report_status, py::call_guard<CustomGuard>());
m.def("multiple_guards_correct_order", []() { m.def(
return CustomGuard::report_status() + std::string(" & ") + DependentGuard::report_status(); "multiple_guards_correct_order",
}, py::call_guard<CustomGuard, DependentGuard>()); []() {
return CustomGuard::report_status() + std::string(" & ")
m.def("multiple_guards_wrong_order", []() { + DependentGuard::report_status();
return DependentGuard::report_status() + std::string(" & ") + CustomGuard::report_status(); },
}, py::call_guard<DependentGuard, CustomGuard>()); 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) #if defined(WITH_THREAD) && !defined(PYPY_VERSION)
// `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well, // `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,

60
tests/test_callbacks.cpp

@ -7,11 +7,12 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/functional.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; } int dummy_function(int i) { return i + 1; }
@ -20,11 +21,12 @@ TEST_SUBMODULE(callbacks, m) {
m.def("test_callback1", [](const py::object &func) { return func(); }); m.def("test_callback1", [](const py::object &func) { return func(); });
m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); }); m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); });
m.def("test_callback3", [](const std::function<int(int)> &func) { m.def("test_callback3", [](const std::function<int(int)> &func) {
return "func(43) = " + std::to_string(func(43)); }); 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"));
}); });
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 // test_keyword_args_and_generalized_unpacking
m.def("test_tuple_unpacking", [](const py::function &f) { m.def("test_tuple_unpacking", [](const py::function &f) {
@ -34,9 +36,9 @@ TEST_SUBMODULE(callbacks, m) {
}); });
m.def("test_dict_unpacking", [](const py::function &f) { 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 d2 = py::dict();
auto d3 = py::dict("b"_a=2); auto d3 = py::dict("b"_a = 2);
return f("positional", 1, **d1, **d2, **d3); return f("positional", 1, **d1, **d2, **d3);
}); });
@ -44,32 +46,40 @@ TEST_SUBMODULE(callbacks, m) {
m.def("test_unpacking_and_keywords1", [](const py::function &f) { m.def("test_unpacking_and_keywords1", [](const py::function &f) {
auto args = py::make_tuple(2); auto args = py::make_tuple(2);
auto kwargs = py::dict("d"_a=4); auto kwargs = py::dict("d"_a = 4);
return f(1, *args, "c"_a=3, **kwargs); return f(1, *args, "c"_a = 3, **kwargs);
}); });
m.def("test_unpacking_and_keywords2", [](const py::function &f) { m.def("test_unpacking_and_keywords2", [](const py::function &f) {
auto kwargs1 = py::dict("a"_a=1); auto kwargs1 = py::dict("a"_a = 1);
auto kwargs2 = py::dict("c"_a=3, "d"_a=4); auto kwargs2 = py::dict("c"_a = 3, "d"_a = 4);
return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5, return f("positional",
"key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5); *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) { m.def("test_unpacking_error1", [](const py::function &f) {
auto kwargs = py::dict("x"_a=3); auto kwargs = py::dict("x"_a = 3);
return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword return f("x"_a = 1, "y"_a = 2, **kwargs); // duplicate ** after keyword
}); });
m.def("test_unpacking_error2", [](const py::function &f) { m.def("test_unpacking_error2", [](const py::function &f) {
auto kwargs = py::dict("x"_a=3); auto kwargs = py::dict("x"_a = 3);
return f(**kwargs, "x"_a=1); // duplicate keyword after ** return f(**kwargs, "x"_a = 1); // duplicate keyword after **
}); });
m.def("test_arg_conversion_error1", m.def("test_arg_conversion_error1",
[](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); }); [](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); });
m.def("test_arg_conversion_error2", [](const py::function &f) { 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 // test_lambda_closure_cleanup
@ -158,7 +168,6 @@ TEST_SUBMODULE(callbacks, m) {
return "matches dummy_function: eval(1) = " + std::to_string(r); return "matches dummy_function: eval(1) = " + std::to_string(r);
} }
return "argument does NOT match dummy_function. This should never happen!"; return "argument does NOT match dummy_function. This should never happen!";
}); });
class AbstractBase { class AbstractBase {
@ -190,7 +199,7 @@ TEST_SUBMODULE(callbacks, m) {
// test_movable_object // test_movable_object
m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) { m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) {
auto x = MovableObject(); 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` return x.valid; // must still return `true`
}); });
@ -202,9 +211,10 @@ TEST_SUBMODULE(callbacks, m) {
// This checks that builtin functions can be passed as callbacks // This checks that builtin functions can be passed as callbacks
// rather than throwing RuntimeError due to trying to extract as capsule // 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) { m.def("test_sum_builtin",
return sum_builtin(i); [](const std::function<double(py::iterable)> &sum_builtin, const py::iterable &i) {
}); return sum_builtin(i);
});
// test async Python callbacks // test async Python callbacks
using callback_f = std::function<void(int)>; using callback_f = std::function<void(int)>;

33
tests/test_chrono.cpp

@ -8,21 +8,20 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/chrono.h> #include <pybind11/chrono.h>
#include "pybind11_tests.h"
#include <chrono> #include <chrono>
struct different_resolutions { struct different_resolutions {
using time_point_h = std::chrono::time_point< using time_point_h = std::chrono::time_point<std::chrono::system_clock, std::chrono::hours>;
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_m = std::chrono::time_point< using time_point_s = std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
std::chrono::system_clock, std::chrono::minutes>; using time_point_ms
using time_point_s = std::chrono::time_point< = std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>;
std::chrono::system_clock, std::chrono::seconds>; using time_point_us
using time_point_ms = std::chrono::time_point< = std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds>;
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_h timestamp_h;
time_point_m timestamp_m; time_point_m timestamp_m;
time_point_s timestamp_s; time_point_s timestamp_s;
@ -65,12 +64,11 @@ TEST_SUBMODULE(chrono, m) {
// Roundtrip a duration in microseconds from a float argument // Roundtrip a duration in microseconds from a float argument
m.def("test_chrono7", [](std::chrono::microseconds t) { return t; }); m.def("test_chrono7", [](std::chrono::microseconds t) { return t; });
// Float durations (issue #719) // Float durations (issue #719)
m.def("test_chrono_float_diff", [](std::chrono::duration<float> a, std::chrono::duration<float> b) { m.def("test_chrono_float_diff",
return a - b; }); [](std::chrono::duration<float> a, std::chrono::duration<float> b) { return a - b; });
m.def("test_nano_timepoint", [](timestamp start, timespan delta) -> timestamp { m.def("test_nano_timepoint",
return start + delta; [](timestamp start, timespan delta) -> timestamp { return start + delta; });
});
// Test different resolutions // Test different resolutions
py::class_<different_resolutions>(m, "different_resolutions") py::class_<different_resolutions>(m, "different_resolutions")
@ -79,6 +77,5 @@ TEST_SUBMODULE(chrono, m) {
.def_readwrite("timestamp_m", &different_resolutions::timestamp_m) .def_readwrite("timestamp_m", &different_resolutions::timestamp_m)
.def_readwrite("timestamp_s", &different_resolutions::timestamp_s) .def_readwrite("timestamp_s", &different_resolutions::timestamp_s)
.def_readwrite("timestamp_ms", &different_resolutions::timestamp_ms) .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 @@
// Intel compiler requires a separate header file to support aligned new operators // Intel compiler requires a separate header file to support aligned new operators
// and does not set the __cpp_aligned_new feature macro. // and does not set the __cpp_aligned_new feature macro.
// This header needs to be included before pybind11. // This header needs to be included before pybind11.
#include <aligned_new> # include <aligned_new>
#endif #endif
#include "pybind11_tests.h" #include <pybind11/stl.h>
#include "constructor_stats.h" #include "constructor_stats.h"
#include "local_bindings.h" #include "local_bindings.h"
#include <pybind11/stl.h> #include "pybind11_tests.h"
#include <utility> #include <utility>
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(disable: 4324) # pragma warning(disable : 4324)
// warning C4324: structure was padded due to alignment specifier // warning C4324: structure was padded due to alignment specifier
#endif #endif
@ -75,6 +76,7 @@ TEST_SUBMODULE(class_, m) {
: m_name(name), m_species(species) {} : m_name(name), m_species(species) {}
std::string name() const { return m_name; } std::string name() const { return m_name; }
std::string species() const { return m_species; } std::string species() const { return m_species; }
private: private:
std::string m_name; std::string m_name;
std::string m_species; std::string m_species;
@ -101,27 +103,24 @@ TEST_SUBMODULE(class_, m) {
}; };
py::class_<Pet> pet_class(m, "Pet"); py::class_<Pet> pet_class(m, "Pet");
pet_class pet_class.def(py::init<std::string, std::string>())
.def(py::init<std::string, std::string>())
.def("name", &Pet::name) .def("name", &Pet::name)
.def("species", &Pet::species); .def("species", &Pet::species);
/* One way of declaring a subclass relationship: reference parent's class_ object */ /* One way of declaring a subclass relationship: reference parent's class_ object */
py::class_<Dog>(m, "Dog", pet_class) py::class_<Dog>(m, "Dog", pet_class).def(py::init<std::string>());
.def(py::init<std::string>());
/* Another way of declaring a subclass relationship: reference parent's C++ type */ /* Another way of declaring a subclass relationship: reference parent's C++ type */
py::class_<Rabbit, Pet>(m, "Rabbit") py::class_<Rabbit, Pet>(m, "Rabbit").def(py::init<std::string>());
.def(py::init<std::string>());
/* And another: list parent in class template arguments */ /* And another: list parent in class template arguments */
py::class_<Hamster, Pet>(m, "Hamster") py::class_<Hamster, Pet>(m, "Hamster").def(py::init<std::string>());
.def(py::init<std::string>());
/* Constructors are not inherited by default */ /* Constructors are not inherited by default */
py::class_<Chimera, Pet>(m, "Chimera"); 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(); }); m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
// test_automatic_upcasting // test_automatic_upcasting
@ -131,15 +130,15 @@ TEST_SUBMODULE(class_, m) {
BaseClass(BaseClass &&) = default; BaseClass(BaseClass &&) = default;
virtual ~BaseClass() = default; virtual ~BaseClass() = default;
}; };
struct DerivedClass1 : BaseClass { }; struct DerivedClass1 : BaseClass {};
struct DerivedClass2 : BaseClass { }; struct DerivedClass2 : BaseClass {};
py::class_<BaseClass>(m, "BaseClass").def(py::init<>()); py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>()); py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>()); py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); }); m.def("return_class_1", []() -> BaseClass * { return new DerivedClass1(); });
m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); }); m.def("return_class_2", []() -> BaseClass * { return new DerivedClass2(); });
m.def("return_class_n", [](int n) -> BaseClass * { m.def("return_class_n", [](int n) -> BaseClass * {
if (n == 1) { if (n == 1) {
return new DerivedClass1(); return new DerivedClass1();
@ -149,19 +148,17 @@ TEST_SUBMODULE(class_, m) {
} }
return new BaseClass(); return new BaseClass();
}); });
m.def("return_none", []() -> BaseClass* { return nullptr; }); m.def("return_none", []() -> BaseClass * { return nullptr; });
// test_isinstance // test_isinstance
m.def("check_instances", [](const py::list &l) { m.def("check_instances", [](const py::list &l) {
return py::make_tuple( return py::make_tuple(py::isinstance<py::tuple>(l[0]),
py::isinstance<py::tuple>(l[0]), py::isinstance<py::dict>(l[1]),
py::isinstance<py::dict>(l[1]), py::isinstance<Pet>(l[2]),
py::isinstance<Pet>(l[2]), py::isinstance<Pet>(l[3]),
py::isinstance<Pet>(l[3]), py::isinstance<Dog>(l[4]),
py::isinstance<Dog>(l[4]), py::isinstance<Rabbit>(l[5]),
py::isinstance<Rabbit>(l[5]), py::isinstance<UnregisteredType>(l[6]));
py::isinstance<UnregisteredType>(l[6])
);
}); });
struct Invalid {}; struct Invalid {};
@ -180,18 +177,16 @@ TEST_SUBMODULE(class_, m) {
m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); }); m.def("get_type_of", [](py::object ob) { return py::type::of(std::move(ob)); });
m.def("get_type_classic", [](py::handle h) { m.def("get_type_classic", [](py::handle h) { return h.get_type(); });
return h.get_type();
});
m.def("as_type", [](const py::object &ob) { return py::type(ob); }); m.def("as_type", [](const py::object &ob) { return py::type(ob); });
// test_mismatched_holder // test_mismatched_holder
struct MismatchBase1 { }; struct MismatchBase1 {};
struct MismatchDerived1 : MismatchBase1 { }; struct MismatchDerived1 : MismatchBase1 {};
struct MismatchBase2 { }; struct MismatchBase2 {};
struct MismatchDerived2 : MismatchBase2 { }; struct MismatchDerived2 : MismatchBase2 {};
m.def("mismatched_holder_1", []() { m.def("mismatched_holder_1", []() {
auto mod = py::module_::import("__main__"); auto mod = py::module_::import("__main__");
@ -201,16 +196,14 @@ TEST_SUBMODULE(class_, m) {
m.def("mismatched_holder_2", []() { m.def("mismatched_holder_2", []() {
auto mod = py::module_::import("__main__"); auto mod = py::module_::import("__main__");
py::class_<MismatchBase2>(mod, "MismatchBase2"); py::class_<MismatchBase2>(mod, "MismatchBase2");
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>, MismatchBase2>(
MismatchBase2>(mod, "MismatchDerived2"); mod, "MismatchDerived2");
}); });
// test_override_static // test_override_static
// #511: problem with inheritance + overwritten def_static // #511: problem with inheritance + overwritten def_static
struct MyBase { struct MyBase {
static std::unique_ptr<MyBase> make() { static std::unique_ptr<MyBase> make() { return std::unique_ptr<MyBase>(new MyBase()); }
return std::unique_ptr<MyBase>(new MyBase());
}
}; };
struct MyDerived : MyBase { struct MyDerived : MyBase {
@ -219,8 +212,7 @@ TEST_SUBMODULE(class_, m) {
} }
}; };
py::class_<MyBase>(m, "MyBase") py::class_<MyBase>(m, "MyBase").def_static("make", &MyBase::make);
.def_static("make", &MyBase::make);
py::class_<MyDerived, MyBase>(m, "MyDerived") py::class_<MyDerived, MyBase>(m, "MyDerived")
.def_static("make", &MyDerived::make) .def_static("make", &MyDerived::make)
@ -233,8 +225,7 @@ TEST_SUBMODULE(class_, m) {
explicit ConvertibleFromUserType(UserType u) : i(u.value()) {} explicit ConvertibleFromUserType(UserType u) : i(u.value()) {}
}; };
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType") py::class_<ConvertibleFromUserType>(m, "AcceptsUserType").def(py::init<UserType>());
.def(py::init<UserType>());
py::implicitly_convertible<UserType, ConvertibleFromUserType>(); py::implicitly_convertible<UserType, ConvertibleFromUserType>();
m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; }); m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; });
@ -257,31 +248,60 @@ TEST_SUBMODULE(class_, m) {
}; };
auto *def = new PyMethodDef{"f", f, METH_VARARGS, nullptr}; auto *def = new PyMethodDef{"f", f, METH_VARARGS, nullptr};
py::capsule def_capsule(def, [](void *ptr) { delete reinterpret_cast<PyMethodDef *>(ptr); }); py::capsule def_capsule(def,
return py::reinterpret_steal<py::object>(PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr())); [](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 // test_operator_new_delete
struct HasOpNewDel { struct HasOpNewDel {
std::uint64_t i; 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) {
static void *operator new(size_t s, void *ptr) { py::print("A placement-new", s); return ptr; } py::print("A new", s);
static void operator delete(void *p) { py::print("A delete"); return ::operator delete(p); } 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 { struct HasOpNewDelSize {
std::uint32_t i; 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) {
static void *operator new(size_t s, void *ptr) { py::print("B placement-new", s); return ptr; } py::print("B new", s);
static void operator delete(void *p, size_t s) { py::print("B delete", s); return ::operator delete(p); } 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 { struct AliasedHasOpNewDelSize {
std::uint64_t i; 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) {
static void *operator new(size_t s, void *ptr) { py::print("C placement-new", s); return ptr; } py::print("C new", s);
static void operator delete(void *p, size_t s) { py::print("C delete", s); return ::operator delete(p); } 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; virtual ~AliasedHasOpNewDelSize() = default;
AliasedHasOpNewDelSize() = default; AliasedHasOpNewDelSize() = default;
AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize&) = delete; AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize &) = delete;
}; };
struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize { struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize {
PyAliasedHasOpNewDelSize() = default; PyAliasedHasOpNewDelSize() = default;
@ -290,15 +310,28 @@ TEST_SUBMODULE(class_, m) {
}; };
struct HasOpNewDelBoth { struct HasOpNewDelBoth {
std::uint32_t i[8]; 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) {
static void *operator new(size_t s, void *ptr) { py::print("D placement-new", s); return ptr; } py::print("D new", s);
static void operator delete(void *p) { py::print("D delete"); return ::operator delete(p); } return ::operator new(s);
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, 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_<HasOpNewDel>(m, "HasOpNewDel").def(py::init<>());
py::class_<HasOpNewDelSize>(m, "HasOpNewDelSize").def(py::init<>()); py::class_<HasOpNewDelSize>(m, "HasOpNewDelSize").def(py::init<>());
py::class_<HasOpNewDelBoth>(m, "HasOpNewDelBoth").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.def(py::init<>());
aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize)); aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize));
aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize)); aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize));
@ -352,7 +385,7 @@ TEST_SUBMODULE(class_, m) {
// [workaround(intel)] = default does not work here // [workaround(intel)] = default does not work here
// Removing or defaulting this destructor results in linking errors with the Intel compiler // 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) // (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; using ProtectedB::foo;
}; };
@ -402,8 +435,8 @@ TEST_SUBMODULE(class_, m) {
py::class_<Nested>(base, "Nested") py::class_<Nested>(base, "Nested")
.def(py::init<>()) .def(py::init<>())
.def("fn", [](Nested &, int, NestBase &, Nested &) {}) .def("fn", [](Nested &, int, NestBase &, Nested &) {})
.def("fa", [](Nested &, int, NestBase &, Nested &) {}, .def(
"a"_a, "b"_a, "c"_a); "fa", [](Nested &, int, NestBase &, Nested &) {}, "a"_a, "b"_a, "c"_a);
base.def("g", [](NestBase &, Nested &) {}); base.def("g", [](NestBase &, Nested &) {});
base.def("h", []() { return NestBase(); }); base.def("h", []() { return NestBase(); });
@ -413,21 +446,21 @@ TEST_SUBMODULE(class_, m) {
// generate a useful error message // generate a useful error message
struct NotRegistered {}; 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", [](int) {});
m.def("test_error_after_conversions", m.def("test_error_after_conversions",
[](const StringWrapper &) -> NotRegistered { return {}; }); [](const StringWrapper &) -> NotRegistered { return {}; });
py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>()); py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
py::implicitly_convertible<std::string, StringWrapper>(); py::implicitly_convertible<std::string, StringWrapper>();
#if defined(PYBIND11_CPP17) #if defined(PYBIND11_CPP17)
struct alignas(1024) Aligned { struct alignas(1024) Aligned {
std::uintptr_t ptr() const { return (uintptr_t) this; } std::uintptr_t ptr() const { return (uintptr_t) this; }
}; };
py::class_<Aligned>(m, "Aligned") py::class_<Aligned>(m, "Aligned").def(py::init<>()).def("ptr", &Aligned::ptr);
.def(py::init<>()) #endif
.def("ptr", &Aligned::ptr);
#endif
// test_final // test_final
struct IsFinal final {}; struct IsFinal final {};
@ -440,9 +473,7 @@ TEST_SUBMODULE(class_, m) {
// test_exception_rvalue_abort // test_exception_rvalue_abort
struct PyPrintDestructor { struct PyPrintDestructor {
PyPrintDestructor() = default; PyPrintDestructor() = default;
~PyPrintDestructor() { ~PyPrintDestructor() { py::print("Print from destructor"); }
py::print("Print from destructor");
}
void throw_something() { throw std::runtime_error("error"); } void throw_something() { throw std::runtime_error("error"); }
}; };
py::class_<PyPrintDestructor>(m, "PyPrintDestructor") py::class_<PyPrintDestructor>(m, "PyPrintDestructor")
@ -456,8 +487,7 @@ TEST_SUBMODULE(class_, m) {
.def(py::init([]() { return &samePointer; })); .def(py::init([]() { return &samePointer; }));
struct Empty {}; struct Empty {};
py::class_<Empty>(m, "Empty") py::class_<Empty>(m, "Empty").def(py::init<>());
.def(py::init<>());
// test_base_and_derived_nested_scope // test_base_and_derived_nested_scope
struct BaseWithNested { struct BaseWithNested {
@ -499,12 +529,15 @@ TEST_SUBMODULE(class_, m) {
}); });
} }
template <int N> class BreaksBase { public: template <int N>
class BreaksBase {
public:
virtual ~BreaksBase() = default; virtual ~BreaksBase() = default;
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: // These should all compile just fine:
using DoesntBreak1 = py::class_<BreaksBase<1>, std::unique_ptr<BreaksBase<1>>, BreaksTramp<1>>; 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>>>; using DoesntBreak2 = py::class_<BreaksBase<2>, BreaksTramp<2>, std::unique_ptr<BreaksBase<2>>>;
@ -514,45 +547,83 @@ using DoesntBreak5 = py::class_<BreaksBase<5>>;
using DoesntBreak6 = py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>>; using 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 DoesntBreak7 = py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>>;
using DoesntBreak8 = py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>>; 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, \ #define CHECK_BASE(N) \
"DoesntBreak" #N " has wrong type!") static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<(N)>>::value, \
CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8); "DoesntBreak" #N " has wrong type!")
#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<(N)>>::value, \ 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!") "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, \ #define CHECK_NOALIAS(N) \
"DoesntBreak" #N " has type alias, but shouldn't!") static_assert(!DoesntBreak##N::has_alias \
CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8); && std::is_void<typename DoesntBreak##N::type_alias>::value, \
#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<(N)>>>::value, \ "DoesntBreak" #N " has type alias, but shouldn't!")
"DoesntBreak" #N " has wrong holder_type!") CHECK_ALIAS(1);
CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique); CHECK_ALIAS(2);
CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared); 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, // 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 // though, so that they can be manually tested by uncommenting them (and seeing that compilation
// failures occurs). // failures occurs).
// We have to actually look into the type: the typedef alone isn't enough to instantiate the type: // 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, \ #define CHECK_BROKEN(N) \
"Breaks1 has wrong type!"); static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-(N)>>::value, \
"Breaks1 has wrong type!");
#ifdef PYBIND11_NEVER_DEFINED_EVER #ifdef PYBIND11_NEVER_DEFINED_EVER
// Two holder classes: // 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); CHECK_BROKEN(1);
// Two aliases: // Two aliases:
typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2; typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
CHECK_BROKEN(2); CHECK_BROKEN(2);
// Holder + 2 aliases // 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); CHECK_BROKEN(3);
// Alias + 2 holders // 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); CHECK_BROKEN(4);
// Invalid option (not a subclass or holder) // Invalid option (not a subclass or holder)
typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5; typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5;
CHECK_BROKEN(5); CHECK_BROKEN(5);
// Invalid option: multiple inheritance not supported: // 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; typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
CHECK_BROKEN(8); CHECK_BROKEN(8);
#endif #endif

67
tests/test_constants_and_functions.cpp

@ -12,20 +12,14 @@
enum MyEnum { EFirstEntry = 1, ESecondEntry }; enum MyEnum { EFirstEntry = 1, ESecondEntry };
std::string test_function1() { std::string test_function1() { return "test_function()"; }
return "test_function()";
}
std::string test_function2(MyEnum k) { std::string test_function2(MyEnum k) { return "test_function(enum=" + std::to_string(k) + ")"; }
return "test_function(enum=" + std::to_string(k) + ")";
}
std::string test_function3(int i) { std::string test_function3(int i) { return "test_function(" + std::to_string(i) + ")"; }
return "test_function(" + std::to_string(i) + ")";
}
py::str test_function4() { return "test_function()"; } py::str test_function4() { return "test_function()"; }
py::str test_function4(char *) { return "test_function(char *)"; } py::str test_function4(char *) { return "test_function(char *)"; }
py::str test_function4(int, float) { return "test_function(int, float)"; } py::str test_function4(int, float) { return "test_function(int, float)"; }
py::str test_function4(float, int) { return "test_function(float, int)"; } py::str test_function4(float, int) { return "test_function(float, int)"; }
@ -44,50 +38,50 @@ std::string print_bytes(const py::bytes &bytes) {
return ret; return ret;
} }
// Test that we properly handle C++17 exception specifiers (which are part of the function signature // Test that we properly handle C++17 exception specifiers (which are part of the function
// in C++17). These should all still work before C++17, but don't affect the function signature. // signature in C++17). These should all still work before C++17, but don't affect the function
// signature.
namespace test_exc_sp { namespace test_exc_sp {
// [workaround(intel)] Unable to use noexcept instead of noexcept(true) // [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 // 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). // it fails to compile with a plain noexcept (tested with icc (ICC) 2021.1 Beta 20200827).
#if defined(__INTEL_COMPILER) && defined(PYBIND11_CPP17) #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 #else
int f1(int x) noexcept { return x+1; } int f1(int x) noexcept { return x + 1; }
#endif #endif
int f2(int x) noexcept(true) { return x+2; } int f2(int x) noexcept(true) { return x + 2; }
int f3(int x) noexcept(false) { return x+3; } int f3(int x) noexcept(false) { return x + 3; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated" # pragma GCC diagnostic ignored "-Wdeprecated"
#endif #endif
// NOLINTNEXTLINE(modernize-use-noexcept) // 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) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
struct C { struct C {
int m1(int x) noexcept { return x-1; } int m1(int x) noexcept { return x - 1; }
int m2(int x) const noexcept { return x-2; } int m2(int x) const noexcept { return x - 2; }
int m3(int x) noexcept(true) { return x-3; } int m3(int x) noexcept(true) { return x - 3; }
int m4(int x) const noexcept(true) { return x-4; } int m4(int x) const noexcept(true) { return x - 4; }
int m5(int x) noexcept(false) { return x-5; } int m5(int x) noexcept(false) { return x - 5; }
int m6(int x) const noexcept(false) { return x-6; } int m6(int x) const noexcept(false) { return x - 6; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated" # pragma GCC diagnostic ignored "-Wdeprecated"
#endif #endif
// NOLINTNEXTLINE(modernize-use-noexcept) // NOLINTNEXTLINE(modernize-use-noexcept)
int m7(int x) throw() { return x - 7; } int m7(int x) throw() { return x - 7; }
// NOLINTNEXTLINE(modernize-use-noexcept) // NOLINTNEXTLINE(modernize-use-noexcept)
int m8(int x) const throw() { return x - 8; } int m8(int x) const throw() { return x - 8; }
#if defined(__GNUG__) && !defined(__INTEL_COMPILER) #if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
}; };
} // namespace test_exc_sp } // namespace test_exc_sp
TEST_SUBMODULE(constants_and_functions, m) { TEST_SUBMODULE(constants_and_functions, m) {
// test_constants // test_constants
m.attr("some_constant") = py::int_(14); m.attr("some_constant") = py::int_(14);
@ -129,8 +123,7 @@ TEST_SUBMODULE(constants_and_functions, m) {
.def("m5", &C::m5) .def("m5", &C::m5)
.def("m6", &C::m6) .def("m6", &C::m6)
.def("m7", &C::m7) .def("m7", &C::m7)
.def("m8", &C::m8) .def("m8", &C::m8);
;
m.def("f1", f1); m.def("f1", f1);
m.def("f2", f2); m.def("f2", f2);
#if defined(__INTEL_COMPILER) #if defined(__INTEL_COMPILER)
@ -150,8 +143,12 @@ TEST_SUBMODULE(constants_and_functions, m) {
uint64_t zeros[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 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) { 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 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(
"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("register_with_raising_repr", [](py::module_ m, const py::object &default_value) {
m.def( m.def(

163
tests/test_copy_move.cpp

@ -8,30 +8,33 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
template <typename derived> template <typename derived>
struct empty { struct empty {
static const derived& get_one() { return instance_; } static const derived &get_one() { return instance_; }
static derived instance_; static derived instance_;
}; };
struct lacking_copy_ctor : public empty<lacking_copy_ctor> { struct lacking_copy_ctor : public empty<lacking_copy_ctor> {
lacking_copy_ctor() = default; 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> { struct lacking_move_ctor : public empty<lacking_move_ctor> {
lacking_move_ctor() = default; lacking_move_ctor() = default;
lacking_move_ctor(const lacking_move_ctor& other) = delete; lacking_move_ctor(const lacking_move_ctor &other) = delete;
lacking_move_ctor(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 */ /* Custom type caster move/copy test classes */
class MoveOnlyInt { class MoveOnlyInt {
@ -71,7 +74,11 @@ public:
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
value = c.value; 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); } ~MoveOrCopyInt() { print_destroyed(this); }
int value; int value;
@ -85,32 +92,55 @@ public:
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
value = c.value; 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); } ~CopyOnlyInt() { print_destroyed(this); }
int value; int value;
}; };
PYBIND11_NAMESPACE_BEGIN(pybind11) PYBIND11_NAMESPACE_BEGIN(pybind11)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <> struct type_caster<MoveOnlyInt> { template <>
struct type_caster<MoveOnlyInt> {
PYBIND11_TYPE_CASTER(MoveOnlyInt, const_name("MoveOnlyInt")); PYBIND11_TYPE_CASTER(MoveOnlyInt, const_name("MoveOnlyInt"));
bool load(handle src, bool) { value = MoveOnlyInt(src.cast<int>()); return true; } bool load(handle src, bool) {
static handle cast(const MoveOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } 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")); PYBIND11_TYPE_CASTER(MoveOrCopyInt, const_name("MoveOrCopyInt"));
bool load(handle src, bool) { value = MoveOrCopyInt(src.cast<int>()); return true; } bool load(handle src, bool) {
static handle cast(const MoveOrCopyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } 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: protected:
CopyOnlyInt value; CopyOnlyInt value;
public: public:
static constexpr auto name = const_name("CopyOnlyInt"); static constexpr auto name = const_name("CopyOnlyInt");
bool load(handle src, bool) { value = CopyOnlyInt(src.cast<int>()); return true; } bool load(handle src, bool) {
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); } 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) { static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) {
if (!src) { if (!src) {
return none().release(); return none().release();
@ -119,7 +149,8 @@ public:
} }
explicit operator CopyOnlyInt *() { return &value; } explicit operator CopyOnlyInt *() { return &value; }
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(detail)
PYBIND11_NAMESPACE_END(pybind11) PYBIND11_NAMESPACE_END(pybind11)
@ -127,23 +158,21 @@ PYBIND11_NAMESPACE_END(pybind11)
TEST_SUBMODULE(copy_move_policies, m) { TEST_SUBMODULE(copy_move_policies, m) {
// test_lacking_copy_ctor // test_lacking_copy_ctor
py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor") py::class_<lacking_copy_ctor>(m, "lacking_copy_ctor")
.def_static("get_one", &lacking_copy_ctor::get_one, .def_static("get_one", &lacking_copy_ctor::get_one, py::return_value_policy::copy);
py::return_value_policy::copy);
// test_lacking_move_ctor // test_lacking_move_ctor
py::class_<lacking_move_ctor>(m, "lacking_move_ctor") py::class_<lacking_move_ctor>(m, "lacking_move_ctor")
.def_static("get_one", &lacking_move_ctor::get_one, .def_static("get_one", &lacking_move_ctor::get_one, py::return_value_policy::move);
py::return_value_policy::move);
// test_move_and_copy_casts // test_move_and_copy_casts
// NOLINTNEXTLINE(performance-unnecessary-value-param) // NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("move_and_copy_casts", [](const py::object &o) { m.def("move_and_copy_casts", [](const py::object &o) {
int r = 0; int r = 0;
r += py::cast<MoveOrCopyInt>(o).value; /* moves */ r += py::cast<MoveOrCopyInt>(o).value; /* moves */
r += py::cast<MoveOnlyInt>(o).value; /* moves */ r += py::cast<MoveOnlyInt>(o).value; /* moves */
r += py::cast<CopyOnlyInt>(o).value; /* copies */ r += py::cast<CopyOnlyInt>(o).value; /* copies */
auto m1(py::cast<MoveOrCopyInt>(o)); /* moves */ auto m1(py::cast<MoveOrCopyInt>(o)); /* moves */
auto m2(py::cast<MoveOnlyInt>(o)); /* moves */ auto m2(py::cast<MoveOnlyInt>(o)); /* moves */
auto m3(py::cast<CopyOnlyInt>(o)); /* copies */ auto m3(py::cast<CopyOnlyInt>(o)); /* copies */
r += m1.value + m2.value + m3.value; r += m1.value + m2.value + m3.value;
return r; return r;
@ -157,28 +186,34 @@ TEST_SUBMODULE(copy_move_policies, m) {
// Changing this breaks the existing test: needs careful review. // Changing this breaks the existing test: needs careful review.
// NOLINTNEXTLINE(performance-unnecessary-value-param) // NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("copy_only", [](CopyOnlyInt m) { return m.value; }); m.def("copy_only", [](CopyOnlyInt m) { return m.value; });
m.def("move_pair", [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) { m.def("move_pair",
return p.first.value + p.second.value; [](std::pair<MoveOnlyInt, MoveOrCopyInt> p) { return p.first.value + p.second.value; });
});
m.def("move_tuple", [](std::tuple<MoveOnlyInt, MoveOrCopyInt, MoveOnlyInt> t) { 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; 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) { m.def("copy_tuple", [](std::tuple<CopyOnlyInt, CopyOnlyInt> t) {
return std::get<0>(t).value + std::get<1>(t).value; 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) { m.def("move_copy_nested",
return x.first.value + std::get<0>(x.second.first).value + std::get<1>(x.second.first).value + [](std::pair<MoveOnlyInt,
std::get<0>(std::get<2>(x.second.first)).value + x.second.second.value; 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", []() { m.def("move_and_copy_cstats", []() {
ConstructorStats::gc(); ConstructorStats::gc();
// Reset counts to 0 so that previous tests don't affect later ones: // Reset counts to 0 so that previous tests don't affect later ones:
auto &mc = ConstructorStats::get<MoveOrCopyInt>(); 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>(); 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>(); 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; py::dict d;
d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference); d["MoveOrCopyInt"] = py::cast(mc, py::return_value_policy::reference);
d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference); d["MoveOnlyInt"] = py::cast(mo, py::return_value_policy::reference);
@ -188,18 +223,13 @@ TEST_SUBMODULE(copy_move_policies, m) {
#ifdef PYBIND11_HAS_OPTIONAL #ifdef PYBIND11_HAS_OPTIONAL
// test_move_and_copy_load_optional // test_move_and_copy_load_optional
m.attr("has_optional") = true; m.attr("has_optional") = true;
m.def("move_optional", [](std::optional<MoveOnlyInt> o) { m.def("move_optional", [](std::optional<MoveOnlyInt> o) { return o->value; });
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_or_copy_optional", [](std::optional<MoveOrCopyInt> o) { m.def("move_optional_tuple",
return o->value; [](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("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 #else
m.attr("has_optional") = false; m.attr("has_optional") = false;
#endif #endif
@ -210,6 +240,7 @@ TEST_SUBMODULE(copy_move_policies, m) {
// added later. // added later.
struct PrivateOpNew { struct PrivateOpNew {
int value = 1; int value = 1;
private: private:
void *operator new(size_t bytes) { void *operator new(size_t bytes) {
void *ptr = std::malloc(bytes); void *ptr = std::malloc(bytes);
@ -221,10 +252,13 @@ TEST_SUBMODULE(copy_move_policies, m) {
}; };
py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value); py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
m.def("private_op_new_value", []() { return PrivateOpNew(); }); m.def("private_op_new_value", []() { return PrivateOpNew(); });
m.def("private_op_new_reference", []() -> const PrivateOpNew & { m.def(
static PrivateOpNew x{}; "private_op_new_reference",
return x; []() -> const PrivateOpNew & {
}, py::return_value_policy::reference); static PrivateOpNew x{};
return x;
},
py::return_value_policy::reference);
// test_move_fallback // test_move_fallback
// #389: rvp::move should fall-through to copy on non-movable objects // #389: rvp::move should fall-through to copy on non-movable objects
@ -234,16 +268,25 @@ TEST_SUBMODULE(copy_move_policies, m) {
MoveIssue1(const MoveIssue1 &c) = default; MoveIssue1(const MoveIssue1 &c) = default;
MoveIssue1(MoveIssue1 &&) = delete; 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 { struct MoveIssue2 {
int v; int v;
explicit MoveIssue2(int v) : v{v} {} explicit MoveIssue2(int v) : v{v} {}
MoveIssue2(MoveIssue2 &&) = default; 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` // #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with
m.def("get_moveissue1", [](int i) { return std::unique_ptr<MoveIssue1>(new MoveIssue1(i)); }, py::return_value_policy::move); // `py::return_value_policy::move`
m.def("get_moveissue2", [](int i) { return MoveIssue2(i); }, 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 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
#include "pybind11_tests.h"
// py::arg/py::arg_v testing: these arguments just record their argument when invoked // 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 ArgInspector1 {
class ArgInspector2 { public: std::string arg = "(default arg inspector 2)"; }; public:
class ArgAlwaysConverts { }; std::string arg = "(default arg inspector 1)";
namespace pybind11 { namespace detail { };
template <> struct type_caster<ArgInspector1> { class ArgInspector2 {
public:
std::string arg = "(default arg inspector 2)";
};
class ArgAlwaysConverts {};
namespace pybind11 {
namespace detail {
template <>
struct type_caster<ArgInspector1> {
public: public:
// Classic // Classic
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY #ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
@ -26,9 +33,10 @@ public:
#endif #endif
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
value.arg = "loading ArgInspector1 argument " + value.arg = "loading ArgInspector1 argument " + std::string(convert ? "WITH" : "WITHOUT")
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " + " conversion allowed. "
"Argument value = " + (std::string) str(src); "Argument value = "
+ (std::string) str(src);
return true; return true;
} }
@ -36,14 +44,16 @@ public:
return str(src.arg).release(); return str(src.arg).release();
} }
}; };
template <> struct type_caster<ArgInspector2> { template <>
struct type_caster<ArgInspector2> {
public: public:
PYBIND11_TYPE_CASTER(ArgInspector2, const_name("ArgInspector2")); PYBIND11_TYPE_CASTER(ArgInspector2, const_name("ArgInspector2"));
bool load(handle src, bool convert) { bool load(handle src, bool convert) {
value.arg = "loading ArgInspector2 argument " + value.arg = "loading ArgInspector2 argument " + std::string(convert ? "WITH" : "WITHOUT")
std::string(convert ? "WITH" : "WITHOUT") + " conversion allowed. " + " conversion allowed. "
"Argument value = " + (std::string) str(src); "Argument value = "
+ (std::string) str(src);
return true; return true;
} }
@ -51,13 +61,12 @@ public:
return str(src.arg).release(); return str(src.arg).release();
} }
}; };
template <> struct type_caster<ArgAlwaysConverts> { template <>
struct type_caster<ArgAlwaysConverts> {
public: public:
PYBIND11_TYPE_CASTER(ArgAlwaysConverts, const_name("ArgAlwaysConverts")); PYBIND11_TYPE_CASTER(ArgAlwaysConverts, const_name("ArgAlwaysConverts"));
bool load(handle, bool convert) { bool load(handle, bool convert) { return convert; }
return convert;
}
static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) { static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) {
return py::none().release(); return py::none().release();
@ -73,14 +82,19 @@ public:
~DestructionTester() { print_destroyed(this); } ~DestructionTester() { print_destroyed(this); }
DestructionTester(const DestructionTester &) { print_copy_created(this); } DestructionTester(const DestructionTester &) { print_copy_created(this); }
DestructionTester(DestructionTester &&) noexcept { print_move_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 { DestructionTester &operator=(DestructionTester &&) noexcept {
print_move_assigned(this); print_move_assigned(this);
return *this; return *this;
} }
}; };
namespace pybind11 { namespace detail { namespace pybind11 {
template <> struct type_caster<DestructionTester> { namespace detail {
template <>
struct type_caster<DestructionTester> {
PYBIND11_TYPE_CASTER(DestructionTester, const_name("DestructionTester")); PYBIND11_TYPE_CASTER(DestructionTester, const_name("DestructionTester"));
bool load(handle, bool) { return true; } bool load(handle, bool) { return true; }
@ -115,9 +129,14 @@ TEST_SUBMODULE(custom_type_casters, m) {
py::class_<ArgInspector>(m, "ArgInspector") py::class_<ArgInspector>(m, "ArgInspector")
.def(py::init<>()) .def(py::init<>())
.def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts()) .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("g",
.def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts()) &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( m.def(
"arg_inspect_func", "arg_inspect_func",
[](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) { [](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) {
@ -127,20 +146,33 @@ TEST_SUBMODULE(custom_type_casters, m) {
py::arg_v(nullptr, ArgInspector1()).noconvert(true), py::arg_v(nullptr, ArgInspector1()).noconvert(true),
py::arg() = ArgAlwaysConverts()); py::arg() = ArgAlwaysConverts());
m.def("floats_preferred", [](double f) { return 0.5 * f; }, "f"_a); m.def(
m.def("floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert()); "floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
m.def("ints_preferred", [](int i) { return i / 2; }, "i"_a); m.def(
m.def("ints_only", [](int i) { return i / 2; }, "i"_a.noconvert()); "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_custom_caster_destruction
// Test that `take_ownership` works on types with a custom type caster when given a pointer // Test that `take_ownership` works on types with a custom type caster when given a pointer
// default policy: don't take ownership: // 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(); }, m.def(
py::return_value_policy::take_ownership); // Takes ownership: destroy when finished "custom_caster_destroy",
m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); }, []() { return new DestructionTester(); },
py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction) py::return_value_policy::take_ownership); // Takes ownership: destroy when finished
m.def("destruction_tester_cstats", &ConstructorStats::get<DestructionTester>, py::return_value_policy::reference); 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) {
py::options options; py::options options;
options.disable_function_signatures(); options.disable_function_signatures();
m.def("test_function1", [](int, int) {}, py::arg("a"), py::arg("b")); m.def(
m.def("test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); "test_function1", [](int, int) {}, py::arg("a"), py::arg("b"));
m.def(
m.def("test_overloaded1", [](int) {}, py::arg("i"), "Overload docstring"); "test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
m.def("test_overloaded1", [](double) {}, py::arg("d"));
m.def(
m.def("test_overloaded2", [](int) {}, py::arg("i"), "overload docstring 1"); "test_overloaded1", [](int) {}, py::arg("i"), "Overload docstring");
m.def("test_overloaded2", [](double) {}, py::arg("d"), "overload docstring 2"); m.def(
"test_overloaded1", [](double) {}, py::arg("d"));
m.def("test_overloaded3", [](int) {}, py::arg("i"));
m.def("test_overloaded3", [](double) {}, py::arg("d"), "Overload docstr"); 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(); options.enable_function_signatures();
m.def("test_function3", [](int, int) {}, py::arg("a"), py::arg("b")); m.def(
m.def("test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring"); "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(); 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; py::options nested_options;
nested_options.enable_user_defined_docstrings(); 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; py::options options;
@ -63,7 +80,9 @@ TEST_SUBMODULE(docstring_options, m) {
int getValue() const { return value; } int getValue() const { return value; }
}; };
py::class_<DocstringTestFoo>(m, "DocstringTestFoo", "This is a class docstring") 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 @@
BSD-style license that can be found in the LICENSE file. 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/eigen.h>
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
#if defined(_MSC_VER) #if defined(_MSC_VER)
#if _MSC_VER < 1910 // VS 2015's MSVC # if _MSC_VER < 1910 // VS 2015's MSVC
# pragma warning(disable: 4127) // C4127: conditional expression is constant # pragma warning(disable : 4127) // C4127: conditional expression is constant
#endif # endif
# pragma warning(disable: 4996) // C4996: std::unary_negation is deprecated # pragma warning(disable : 4996) // C4996: std::unary_negation is deprecated
#endif #endif
#include <Eigen/Cholesky> #include <Eigen/Cholesky>
using MatrixXdR = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; 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 // Sets/resets a testing reference matrix to have values of 10*r + c, where r and c are the
// (1-based) row/column number. // (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 i = 0; i < x.rows(); i++) {
for (int j = 0; j < x.cols(); j++) { for (int j = 0; j < x.cols(); j++) {
x(i, j) = 11 + 10 * i + j; x(i, j) = 11 + 10 * i + j;
@ -64,7 +64,8 @@ double get_elem(const Eigen::Ref<const Eigen::MatrixXd> &m) { return m(2, 1); };
// Returns a matrix with 10*r + 100*c added to each matrix element (to help test that the matrix // 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). // 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); Eigen::MatrixXd ret(m);
for (int c = 0; c < m.cols(); c++) { for (int c = 0; c < m.cols(); c++) {
for (int r = 0; r < m.rows(); r++) { for (int r = 0; r < m.rows(); r++) {
@ -97,8 +98,10 @@ TEST_SUBMODULE(eigen, m) {
// various tests // various tests
m.def("double_col", [](const Eigen::VectorXf &x) -> Eigen::VectorXf { return 2.0f * x; }); 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_row",
m.def("double_complex", [](const Eigen::VectorXcf &x) -> Eigen::VectorXcf { return 2.0f * x; }); [](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_threec", [](py::EigenDRef<Eigen::Vector3f> x) { x *= 2; });
m.def("double_threer", [](py::EigenDRef<Eigen::RowVector3f> 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; }); m.def("double_mat_cm", [](const Eigen::MatrixXf &x) -> Eigen::MatrixXf { return 2.0f * x; });
@ -108,19 +111,22 @@ TEST_SUBMODULE(eigen, m) {
// Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended // Different ways of passing via Eigen::Ref; the first and second are the Eigen-recommended
m.def("cholesky1", m.def("cholesky1",
[](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); [](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("cholesky2", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd {
m.def("cholesky3", [](const Eigen::Ref<MatrixXdR> &x) -> Eigen::MatrixXd { return x.llt().matrixL(); }); 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 { m.def("cholesky4", [](const Eigen::Ref<const MatrixXdR> &x) -> Eigen::MatrixXd {
return x.llt().matrixL(); return x.llt().matrixL();
}); });
// test_eigen_ref_mutators // test_eigen_ref_mutators
// Mutators: these add some value to the given element using Eigen, but Eigen should be mapping into // Mutators: these add some value to the given element using Eigen, but Eigen should be mapping
// the numpy array data and so the result should show up there. There are three versions: one that // into the numpy array data and so the result should show up there. There are three versions:
// works on a contiguous-row matrix (numpy's default), one for a contiguous-column matrix, and one // one that works on a contiguous-row matrix (numpy's default), one for a contiguous-column
// for any matrix. // 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_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; }; auto add_cm = [](Eigen::Ref<Eigen::MatrixXd> x, int r, int c, double v) { x(r, c) += v; };
// Mutators (Eigen maps into numpy variables): // Mutators (Eigen maps into numpy variables):
m.def("add_rm", add_rm); // Only takes row-contiguous m.def("add_rm", add_rm); // Only takes row-contiguous
@ -131,7 +137,8 @@ TEST_SUBMODULE(eigen, m) {
m.def("add2", add_cm); m.def("add2", add_cm);
m.def("add2", add_rm); m.def("add2", add_rm);
// This one accepts a matrix of any stride: // 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) // Return mutable references (numpy maps into eigen variables)
m.def("get_cm_ref", []() { return Eigen::Ref<Eigen::MatrixXd>(get_cm()); }); m.def("get_cm_ref", []() { return Eigen::Ref<Eigen::MatrixXd>(get_cm()); });
@ -143,45 +150,67 @@ TEST_SUBMODULE(eigen, m) {
m.def("reset_refs", reset_refs); // Restores get_{cm,rm}_ref to original values m.def("reset_refs", reset_refs); // Restores get_{cm,rm}_ref to original values
// Increments and returns ref to (same) matrix // Increments and returns ref to (same) matrix
m.def("incr_matrix", [](Eigen::Ref<Eigen::MatrixXd> m, double v) { m.def(
m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v); "incr_matrix",
return m; [](Eigen::Ref<Eigen::MatrixXd> m, double v) {
}, py::return_value_policy::reference); m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v);
return m;
},
py::return_value_policy::reference);
// Same, but accepts a matrix of any strides // Same, but accepts a matrix of any strides
m.def("incr_matrix_any", [](py::EigenDRef<Eigen::MatrixXd> m, double v) { m.def(
m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v); "incr_matrix_any",
return m; [](py::EigenDRef<Eigen::MatrixXd> m, double v) {
}, py::return_value_policy::reference); m += Eigen::MatrixXd::Constant(m.rows(), m.cols(), v);
return m;
},
py::return_value_policy::reference);
// Returns an eigen slice of even rows // Returns an eigen slice of even rows
m.def("even_rows", [](py::EigenDRef<Eigen::MatrixXd> m) { m.def(
return py::EigenDMap<Eigen::MatrixXd>( "even_rows",
m.data(), (m.rows() + 1) / 2, m.cols(), [](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::EigenDStride(m.outerStride(), 2 * m.innerStride()));
}, py::return_value_policy::reference); },
py::return_value_policy::reference);
// Returns an eigen slice of even columns // Returns an eigen slice of even columns
m.def("even_cols", [](py::EigenDRef<Eigen::MatrixXd> m) { m.def(
return py::EigenDMap<Eigen::MatrixXd>( "even_cols",
m.data(), m.rows(), (m.cols() + 1) / 2, [](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::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 // 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", [](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_1",
m.def("diagonal_n", [](const Eigen::Ref<const Eigen::MatrixXd> &x, int index) { return x.diagonal(index); }); [](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) // 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) { m.def("block",
return x.block(start_row, start_col, block_rows, block_cols); [](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 // test_eigen_return_references, test_eigen_keepalive
// return value referencing/copying tests: // return value referencing/copying tests:
class ReturnTester { class ReturnTester {
Eigen::MatrixXd mat = create(); Eigen::MatrixXd mat = create();
public: public:
ReturnTester() { print_created(this); } ReturnTester() { print_created(this); }
~ReturnTester() { print_destroyed(this); } ~ReturnTester() { print_destroyed(this); }
@ -194,12 +223,24 @@ TEST_SUBMODULE(eigen, m) {
const Eigen::MatrixXd *viewPtr() { return &mat; } const Eigen::MatrixXd *viewPtr() { return &mat; }
Eigen::Ref<Eigen::MatrixXd> ref() { return mat; } Eigen::Ref<Eigen::MatrixXd> ref() { return mat; }
Eigen::Ref<const Eigen::MatrixXd> refConst() { 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<Eigen::MatrixXd> block(int r, int c, int nrow, int ncol) {
Eigen::Block<const Eigen::MatrixXd> blockConst(int r, int c, int nrow, int ncol) const { return mat.block(r, c, nrow, ncol); } 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))); } Eigen::Block<const Eigen::MatrixXd> blockConst(int r, int c, int nrow, int ncol) const {
py::EigenDMap<const Eigen::Matrix2d> cornersConst() const { return py::EigenDMap<const Eigen::Matrix2d>(mat.data(), return mat.block(r, c, nrow, ncol);
py::EigenDStride(mat.outerStride() * (mat.outerSize()-1), mat.innerStride() * (mat.innerSize()-1))); } }
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; using rvp = py::return_value_policy;
py::class_<ReturnTester>(m, "ReturnTester") py::class_<ReturnTester>(m, "ReturnTester")
@ -210,9 +251,9 @@ TEST_SUBMODULE(eigen, m) {
.def("get_ptr", &ReturnTester::getPtr, rvp::reference_internal) .def("get_ptr", &ReturnTester::getPtr, rvp::reference_internal)
.def("view", &ReturnTester::view, rvp::reference_internal) .def("view", &ReturnTester::view, rvp::reference_internal)
.def("view_ptr", &ReturnTester::view, rvp::reference_internal) .def("view_ptr", &ReturnTester::view, rvp::reference_internal)
.def("copy_get", &ReturnTester::get) // Default rvp: copy .def("copy_get", &ReturnTester::get) // Default rvp: copy
.def("copy_view", &ReturnTester::view) // " .def("copy_view", &ReturnTester::view) // "
.def("ref", &ReturnTester::ref) // Default for Ref is to reference .def("ref", &ReturnTester::ref) // Default for Ref is to reference
.def("ref_const", &ReturnTester::refConst) // Likewise, but const .def("ref_const", &ReturnTester::refConst) // Likewise, but const
.def("ref_safe", &ReturnTester::ref, rvp::reference_internal) .def("ref_safe", &ReturnTester::ref, rvp::reference_internal)
.def("ref_const_safe", &ReturnTester::refConst, rvp::reference_internal) .def("ref_const_safe", &ReturnTester::refConst, rvp::reference_internal)
@ -223,8 +264,7 @@ TEST_SUBMODULE(eigen, m) {
.def("block_const", &ReturnTester::blockConst, rvp::reference_internal) .def("block_const", &ReturnTester::blockConst, rvp::reference_internal)
.def("copy_block", &ReturnTester::block, rvp::copy) .def("copy_block", &ReturnTester::block, rvp::copy)
.def("corners", &ReturnTester::corners, rvp::reference_internal) .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 // test_special_matrix_objects
// Returns a DiagonalMatrix with diagonal (1,2,3,...) // Returns a DiagonalMatrix with diagonal (1,2,3,...)
@ -237,21 +277,16 @@ TEST_SUBMODULE(eigen, m) {
}); });
// Returns a SelfAdjointView referencing the lower triangle of m // Returns a SelfAdjointView referencing the lower triangle of m
m.def("symmetric_lower", [](const Eigen::MatrixXi &m) { m.def("symmetric_lower",
return m.selfadjointView<Eigen::Lower>(); [](const Eigen::MatrixXi &m) { return m.selfadjointView<Eigen::Lower>(); });
});
// Returns a SelfAdjointView referencing the lower triangle of m // Returns a SelfAdjointView referencing the lower triangle of m
m.def("symmetric_upper", [](const Eigen::MatrixXi &m) { m.def("symmetric_upper",
return m.selfadjointView<Eigen::Upper>(); [](const Eigen::MatrixXi &m) { return m.selfadjointView<Eigen::Upper>(); });
});
// Test matrix for various functions below. // Test matrix for various functions below.
Eigen::MatrixXf mat(5, 6); Eigen::MatrixXf mat(5, 6);
mat << 0, 3, 0, 0, 0, 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,
22, 0, 0, 0, 17, 11, 0, 8, 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 // test_fixed, and various other tests
m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); }); m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); });
@ -276,7 +311,8 @@ TEST_SUBMODULE(eigen, m) {
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return Eigen::SparseView<Eigen::MatrixXf>(mat); 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_r", [](const SparseMatrixR &m) -> SparseMatrixR { return m; });
m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; }); m.def("sparse_copy_c", [](const SparseMatrixC &m) -> SparseMatrixC { return m; });
// test_partially_fixed // test_partially_fixed
@ -290,7 +326,8 @@ TEST_SUBMODULE(eigen, m) {
m.def("cpp_copy", [](py::handle m) { return m.cast<Eigen::MatrixXd>()(1, 0); }); m.def("cpp_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_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_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. // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
@ -304,15 +341,23 @@ TEST_SUBMODULE(eigen, m) {
[](const Eigen::Ref<const Eigen::MatrixXd> &m) -> double { return get_elem(m); }, [](const Eigen::Ref<const Eigen::MatrixXd> &m) -> double { return get_elem(m); },
py::arg{}.noconvert()); py::arg{}.noconvert());
// Also test a row-major-only no-copy const ref: // 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); }, m.def(
py::arg{}.noconvert()); "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 // test_issue738
// Issue #738: 1xN or Nx1 2D matrices were neither accepted nor properly copied with an // 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 // 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. // 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_f1",
m.def("iss738_f2", &adjust_matrix<const Eigen::Ref<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>> &>, py::arg{}.noconvert()); &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 // test_issue1105
// Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense // 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 @@
#include <pybind11/embed.h> #include <pybind11/embed.h>
#ifdef _MSC_VER #ifdef _MSC_VER
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch // Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
// 2.0.1; this should be fixed in the next catch release after 2.0.1). // catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
# pragma warning(disable: 4996) # pragma warning(disable : 4996)
#endif #endif
// Catch uses _ internally, which breaks gettext style defines // Catch uses _ internally, which breaks gettext style defines
#ifdef _ #ifdef _
#undef _ # undef _
#endif #endif
#define CATCH_CONFIG_RUNNER #define CATCH_CONFIG_RUNNER

9
tests/test_embed/external_module.cpp

@ -13,11 +13,8 @@ PYBIND11_MODULE(external_module, m) {
int v; int v;
}; };
py::class_<A>(m, "A") py::class_<A>(m, "A").def(py::init<int>()).def_readwrite("value", &A::v);
.def(py::init<int>())
.def_readwrite("value", &A::v);
m.def("internals_at", []() { m.def("internals_at",
return reinterpret_cast<uintptr_t>(&py::detail::get_internals()); []() { return reinterpret_cast<uintptr_t>(&py::detail::get_internals()); });
});
} }

59
tests/test_embed/test_interpreter.cpp

@ -1,13 +1,12 @@
#include <pybind11/embed.h> #include <pybind11/embed.h>
#ifdef _MSC_VER #ifdef _MSC_VER
// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch // Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to
// 2.0.1; this should be fixed in the next catch release after 2.0.1). // catch 2.0.1; this should be fixed in the next catch release after 2.0.1).
# pragma warning(disable: 4996) # pragma warning(disable : 4996)
#endif #endif
#include <catch.hpp> #include <catch.hpp>
#include <cstdlib> #include <cstdlib>
#include <fstream> #include <fstream>
#include <functional> #include <functional>
@ -62,14 +61,14 @@ PYBIND11_EMBEDDED_MODULE(widget_module, m) {
} }
PYBIND11_EMBEDDED_MODULE(trampoline_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(py::init_alias<>())
.def("func", &test_override_cache_helper::func); .def("func", &test_override_cache_helper::func);
} }
PYBIND11_EMBEDDED_MODULE(throw_exception, ) { PYBIND11_EMBEDDED_MODULE(throw_exception, ) { throw std::runtime_error("C++ Error"); }
throw std::runtime_error("C++ Error");
}
PYBIND11_EMBEDDED_MODULE(throw_error_already_set, ) { PYBIND11_EMBEDDED_MODULE(throw_error_already_set, ) {
auto d = py::dict(); auto d = py::dict();
@ -80,11 +79,13 @@ TEST_CASE("Pass classes and data between modules defined in C++ and Python") {
auto module_ = py::module_::import("test_interpreter"); auto module_ = py::module_::import("test_interpreter");
REQUIRE(py::hasattr(module_, "DerivedWidget")); 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"( py::exec(R"(
widget = DerivedWidget("{} - {}".format(hello, x)) widget = DerivedWidget("{} - {}".format(hello, x))
message = widget.the_message message = widget.the_message
)", py::globals(), locals); )",
py::globals(),
locals);
REQUIRE(locals["message"].cast<std::string>() == "Hello, World! - 5"); REQUIRE(locals["message"].cast<std::string>() == "Hello, World! - 5");
auto py_widget = module_.attr("DerivedWidget")("The question"); auto py_widget = module_.attr("DerivedWidget")("The question");
@ -124,20 +125,21 @@ TEST_CASE("Override cache") {
TEST_CASE("Import error handling") { TEST_CASE("Import error handling") {
REQUIRE_NOTHROW(py::module_::import("widget_module")); REQUIRE_NOTHROW(py::module_::import("widget_module"));
REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), "ImportError: C++ Error");
"ImportError: C++ Error");
#if PY_VERSION_HEX >= 0x03030000 #if PY_VERSION_HEX >= 0x03030000
REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"), REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
Catch::Contains("ImportError: initialization failed")); 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"( py::exec(R"(
try: try:
import throw_error_already_set import throw_error_already_set
except ImportError as e: except ImportError as e:
is_keyerror = type(e.__cause__) == KeyError is_keyerror = type(e.__cause__) == KeyError
message = str(e.__cause__) message = str(e.__cause__)
)", py::globals(), locals); )",
py::globals(),
locals);
REQUIRE(locals["is_keyerror"].cast<bool>() == true); REQUIRE(locals["is_keyerror"].cast<bool>() == true);
REQUIRE(locals["message"].cast<std::string>() == "'missing'"); REQUIRE(locals["message"].cast<std::string>() == "'missing'");
#else #else
@ -179,11 +181,12 @@ TEST_CASE("Restart the interpreter") {
REQUIRE(py::module_::import("widget_module").attr("add")(1, 2).cast<int>() == 3); REQUIRE(py::module_::import("widget_module").attr("add")(1, 2).cast<int>() == 3);
REQUIRE(has_pybind11_internals_builtin()); REQUIRE(has_pybind11_internals_builtin());
REQUIRE(has_pybind11_internals_static()); 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: // local and foreign module internals should point to the same internals:
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) == REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp())
py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>()); == py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>());
// Restart the interpreter. // Restart the interpreter.
py::finalize_interpreter(); py::finalize_interpreter();
@ -198,16 +201,19 @@ TEST_CASE("Restart the interpreter") {
pybind11::detail::get_internals(); pybind11::detail::get_internals();
REQUIRE(has_pybind11_internals_builtin()); REQUIRE(has_pybind11_internals_builtin());
REQUIRE(has_pybind11_internals_static()); REQUIRE(has_pybind11_internals_static());
REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) == REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp())
py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>()); == 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 // Make sure that an interpreter with no get_internals() created until finalize still gets the
// internals destroyed // internals destroyed
py::finalize_interpreter(); py::finalize_interpreter();
py::initialize_interpreter(); py::initialize_interpreter();
bool ran = false; bool ran = false;
py::module_::import("__main__").attr("internals_destroy_test") = py::module_::import("__main__").attr("internals_destroy_test")
py::capsule(&ran, [](void *ran) { py::detail::get_internals(); *static_cast<bool *>(ran) = true; }); = 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_builtin());
REQUIRE_FALSE(has_pybind11_internals_static()); REQUIRE_FALSE(has_pybind11_internals_static());
REQUIRE_FALSE(ran); REQUIRE_FALSE(ran);
@ -281,7 +287,7 @@ TEST_CASE("Threads") {
REQUIRE_FALSE(has_pybind11_internals_static()); REQUIRE_FALSE(has_pybind11_internals_static());
constexpr auto num_threads = 10; 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{}; py::gil_scoped_release gil_release{};
@ -322,9 +328,8 @@ TEST_CASE("Reload module from file") {
bool dont_write_bytecode = sys.attr("dont_write_bytecode").cast<bool>(); bool dont_write_bytecode = sys.attr("dont_write_bytecode").cast<bool>();
sys.attr("dont_write_bytecode") = true; sys.attr("dont_write_bytecode") = true;
// Reset the value at scope exit // Reset the value at scope exit
scope_exit reset_dont_write_bytecode([&]() { scope_exit reset_dont_write_bytecode(
sys.attr("dont_write_bytecode") = dont_write_bytecode; [&]() { sys.attr("dont_write_bytecode") = dont_write_bytecode; });
});
std::string module_name = "test_module_reload"; std::string module_name = "test_module_reload";
std::string module_file = module_name + ".py"; std::string module_file = module_name + ".py";
@ -335,9 +340,7 @@ TEST_CASE("Reload module from file") {
test_module << " return 1\n"; test_module << " return 1\n";
test_module.close(); test_module.close();
// Delete the file at scope exit // Delete the file at scope exit
scope_exit delete_module_file([&]() { scope_exit delete_module_file([&]() { std::remove(module_file.c_str()); });
std::remove(module_file.c_str());
});
// Import the module from file // Import the module from file
auto module_ = py::module_::import(module_name.c_str()); auto module_ = py::module_::import(module_name.c_str());

83
tests/test_enum.cpp

@ -11,11 +11,7 @@
TEST_SUBMODULE(enums, m) { TEST_SUBMODULE(enums, m) {
// test_unscoped_enum // test_unscoped_enum
enum UnscopedEnum { enum UnscopedEnum { EOne = 1, ETwo, EThree };
EOne = 1,
ETwo,
EThree
};
py::enum_<UnscopedEnum>(m, "UnscopedEnum", py::arithmetic(), "An unscoped enumeration") py::enum_<UnscopedEnum>(m, "UnscopedEnum", py::arithmetic(), "An unscoped enumeration")
.value("EOne", EOne, "Docstring for EOne") .value("EOne", EOne, "Docstring for EOne")
.value("ETwo", ETwo, "Docstring for ETwo") .value("ETwo", ETwo, "Docstring for ETwo")
@ -23,10 +19,7 @@ TEST_SUBMODULE(enums, m) {
.export_values(); .export_values();
// test_scoped_enum // test_scoped_enum
enum class ScopedEnum { enum class ScopedEnum { Two = 2, Three };
Two = 2,
Three
};
py::enum_<ScopedEnum>(m, "ScopedEnum", py::arithmetic()) py::enum_<ScopedEnum>(m, "ScopedEnum", py::arithmetic())
.value("Two", ScopedEnum::Two) .value("Two", ScopedEnum::Two)
.value("Three", ScopedEnum::Three); .value("Three", ScopedEnum::Three);
@ -36,11 +29,7 @@ TEST_SUBMODULE(enums, m) {
}); });
// test_binary_operators // test_binary_operators
enum Flags { enum Flags { Read = 4, Write = 2, Execute = 1 };
Read = 4,
Write = 2,
Execute = 1
};
py::enum_<Flags>(m, "Flags", py::arithmetic()) py::enum_<Flags>(m, "Flags", py::arithmetic())
.value("Read", Flags::Read) .value("Read", Flags::Read)
.value("Write", Flags::Write) .value("Write", Flags::Write)
@ -50,14 +39,9 @@ TEST_SUBMODULE(enums, m) {
// test_implicit_conversion // test_implicit_conversion
class ClassWithUnscopedEnum { class ClassWithUnscopedEnum {
public: public:
enum EMode { enum EMode { EFirstMode = 1, ESecondMode };
EFirstMode = 1,
ESecondMode static EMode test_function(EMode mode) { return mode; }
};
static EMode test_function(EMode mode) {
return mode;
}
}; };
py::class_<ClassWithUnscopedEnum> exenum_class(m, "ClassWithUnscopedEnum"); py::class_<ClassWithUnscopedEnum> exenum_class(m, "ClassWithUnscopedEnum");
exenum_class.def_static("test_function", &ClassWithUnscopedEnum::test_function); exenum_class.def_static("test_function", &ClassWithUnscopedEnum::test_function);
@ -67,19 +51,17 @@ TEST_SUBMODULE(enums, m) {
.export_values(); .export_values();
// test_enum_to_int // test_enum_to_int
m.def("test_enum_to_int", [](int) { }); m.def("test_enum_to_int", [](int) {});
m.def("test_enum_to_uint", [](uint32_t) { }); m.def("test_enum_to_uint", [](uint32_t) {});
m.def("test_enum_to_long_long", [](long long) { }); m.def("test_enum_to_long_long", [](long long) {});
// test_duplicate_enum_name // test_duplicate_enum_name
enum SimpleEnum enum SimpleEnum { ONE, TWO, THREE };
{
ONE, TWO, THREE
};
m.def("register_bad_enum", [m]() { m.def("register_bad_enum", [m]() {
py::enum_<SimpleEnum>(m, "SimpleEnum") 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::TWO)
.value("ONE", SimpleEnum::THREE) .value("ONE", SimpleEnum::THREE)
.export_values(); .export_values();
@ -90,33 +72,36 @@ TEST_SUBMODULE(enums, m) {
enum class ScopedShortEnum : short {}; enum class ScopedShortEnum : short {};
enum class ScopedLongEnum : long {}; enum class ScopedLongEnum : long {};
enum UnscopedUInt64Enum : std::uint64_t {}; enum UnscopedUInt64Enum : std::uint64_t {};
static_assert(py::detail::all_of< static_assert(
std::is_same<py::enum_<UnscopedUCharEnum>::Scalar, unsigned char>, py::detail::all_of<
std::is_same<py::enum_<ScopedShortEnum>::Scalar, short>, std::is_same<py::enum_<UnscopedUCharEnum>::Scalar, unsigned char>,
std::is_same<py::enum_<ScopedLongEnum>::Scalar, long>, std::is_same<py::enum_<ScopedShortEnum>::Scalar, short>,
std::is_same<py::enum_<UnscopedUInt64Enum>::Scalar, std::uint64_t> std::is_same<py::enum_<ScopedLongEnum>::Scalar, long>,
>::value, "Error during the deduction of enum's scalar type with normal integer underlying"); 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 // test_enum_scalar_with_char_underlying
enum class ScopedCharEnum : char { Zero, Positive }; enum class ScopedCharEnum : char { Zero, Positive };
enum class ScopedWCharEnum : wchar_t { Zero, Positive }; enum class ScopedWCharEnum : wchar_t { Zero, Positive };
enum class ScopedChar32Enum : char32_t { Zero, Positive }; enum class ScopedChar32Enum : char32_t { Zero, Positive };
enum class ScopedChar16Enum : char16_t { Zero, Positive }; enum class ScopedChar16Enum : char16_t { Zero, Positive };
// test the scalar of char type enums according to chapter 'Character types' // test the scalar of char type enums according to chapter 'Character types'
// from https://en.cppreference.com/w/cpp/language/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( static_assert(
sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 2 || py::detail::any_of<
sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 4 std::is_same<py::enum_<ScopedCharEnum>::Scalar, signed char>, // e.g. gcc on x86
, "wchar_t should be either 16 bits (Windows) or 32 (everywhere else)"); std::is_same<py::enum_<ScopedCharEnum>::Scalar, unsigned char> // e.g. arm linux
static_assert(py::detail::all_of< >::value,
std::is_same<py::enum_<ScopedChar32Enum>::Scalar, std::uint_least32_t>, "char should be cast to either signed char or unsigned char");
std::is_same<py::enum_<ScopedChar16Enum>::Scalar, std::uint_least16_t> static_assert(sizeof(py::enum_<ScopedWCharEnum>::Scalar) == 2
>::value, "char32_t, char16_t (and char8_t)'s size, signedness, and alignment is determined"); || 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) #if defined(PYBIND11_HAS_U8STRING)
enum class ScopedChar8Enum : char8_t { Zero, Positive }; enum class ScopedChar8Enum : char8_t { Zero, Positive };
static_assert(std::is_same<py::enum_<ScopedChar8Enum>::Scalar, unsigned char>::value); static_assert(std::is_same<py::enum_<ScopedChar8Enum>::Scalar, unsigned char>::value);

28
tests/test_eval.cpp

@ -7,10 +7,10 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include <pybind11/eval.h> #include <pybind11/eval.h>
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <utility> #include <utility>
TEST_SUBMODULE(eval_, m) { TEST_SUBMODULE(eval_, m) {
@ -20,16 +20,13 @@ TEST_SUBMODULE(eval_, m) {
m.def("test_eval_statements", [global]() { m.def("test_eval_statements", [global]() {
auto local = py::dict(); auto local = py::dict();
local["call_test"] = py::cpp_function([&]() -> int { local["call_test"] = py::cpp_function([&]() -> int { return 42; });
return 42;
});
// Regular string literal // Regular string literal
py::exec( py::exec("message = 'Hello World!'\n"
"message = 'Hello World!'\n" "x = call_test()",
"x = call_test()", global,
global, local local);
);
// Multi-line raw string literal // Multi-line raw string literal
py::exec(R"( py::exec(R"(
@ -37,8 +34,9 @@ TEST_SUBMODULE(eval_, m) {
print(message) print(message)
else: else:
raise RuntimeError raise RuntimeError
)", global, local )",
); global,
local);
auto x = local["x"].cast<int>(); auto x = local["x"].cast<int>();
return x == 42; return x == 42;
@ -53,9 +51,7 @@ TEST_SUBMODULE(eval_, m) {
m.def("test_eval_single_statement", []() { m.def("test_eval_single_statement", []() {
auto local = py::dict(); auto local = py::dict();
local["call_test"] = py::cpp_function([&]() -> int { local["call_test"] = py::cpp_function([&]() -> int { return 42; });
return 42;
});
auto result = py::eval<py::eval_single_statement>("x = call_test()", py::dict(), local); auto result = py::eval<py::eval_single_statement>("x = call_test()", py::dict(), local);
auto x = local["x"].cast<int>(); auto x = local["x"].cast<int>();
@ -114,7 +110,9 @@ TEST_SUBMODULE(eval_, m) {
def func_local(): def func_local():
return local_value return local_value
)", global, local); )",
global,
local);
return std::make_pair(global, local); return std::make_pair(global, local);
}); });
} }

95
tests/test_exceptions.cpp

@ -9,8 +9,8 @@
#include "test_exceptions.h" #include "test_exceptions.h"
#include "local_bindings.h" #include "local_bindings.h"
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <exception> #include <exception>
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
@ -18,8 +18,9 @@
// A type that should be raised as an exception in Python // A type that should be raised as an exception in Python
class MyException : public std::exception { class MyException : public std::exception {
public: public:
explicit MyException(const char * m) : message{m} {} explicit MyException(const char *m) : message{m} {}
const char * what() const noexcept override {return message.c_str();} const char *what() const noexcept override { return message.c_str(); }
private: private:
std::string message = ""; std::string message = "";
}; };
@ -27,8 +28,9 @@ private:
// A type that should be translated to a standard Python exception // A type that should be translated to a standard Python exception
class MyException2 : public std::exception { class MyException2 : public std::exception {
public: public:
explicit MyException2(const char * m) : message{m} {} explicit MyException2(const char *m) : message{m} {}
const char * what() const noexcept override {return message.c_str();} const char *what() const noexcept override { return message.c_str(); }
private: private:
std::string message = ""; std::string message = "";
}; };
@ -36,13 +38,13 @@ private:
// A type that is not derived from std::exception (and is thus unknown) // A type that is not derived from std::exception (and is thus unknown)
class MyException3 { class MyException3 {
public: public:
explicit MyException3(const char * m) : message{m} {} explicit MyException3(const char *m) : message{m} {}
virtual const char * what() const noexcept {return message.c_str();} virtual const char *what() const noexcept { return message.c_str(); }
// Rule of 5 BEGIN: to preempt compiler warnings. // Rule of 5 BEGIN: to preempt compiler warnings.
MyException3(const MyException3&) = default; MyException3(const MyException3 &) = default;
MyException3(MyException3&&) = default; MyException3(MyException3 &&) = default;
MyException3& operator=(const MyException3&) = default; MyException3 &operator=(const MyException3 &) = default;
MyException3& operator=(MyException3&&) = default; MyException3 &operator=(MyException3 &&) = default;
virtual ~MyException3() = default; virtual ~MyException3() = default;
// Rule of 5 END. // Rule of 5 END.
private: private:
@ -53,13 +55,13 @@ private:
// and delegated to its exception translator // and delegated to its exception translator
class MyException4 : public std::exception { class MyException4 : public std::exception {
public: public:
explicit MyException4(const char * m) : message{m} {} explicit MyException4(const char *m) : message{m} {}
const char * what() const noexcept override {return message.c_str();} const char *what() const noexcept override { return message.c_str(); }
private: private:
std::string message = ""; std::string message = "";
}; };
// Like the above, but declared via the helper function // Like the above, but declared via the helper function
class MyException5 : public std::logic_error { class MyException5 : public std::logic_error {
public: public:
@ -71,17 +73,16 @@ class MyException5_1 : public MyException5 {
using MyException5::MyException5; using MyException5::MyException5;
}; };
// Exception that will be caught via the module local translator. // Exception that will be caught via the module local translator.
class MyException6 : public std::exception { class MyException6 : public std::exception {
public: public:
explicit MyException6(const char * m) : message{m} {} explicit MyException6(const char *m) : message{m} {}
const char * what() const noexcept override {return message.c_str();} const char *what() const noexcept override { return message.c_str(); }
private: private:
std::string message = ""; std::string message = "";
}; };
struct PythonCallInDestructor { struct PythonCallInDestructor {
explicit PythonCallInDestructor(const py::dict &d) : d(d) {} explicit PythonCallInDestructor(const py::dict &d) : d(d) {}
~PythonCallInDestructor() { d["good"] = true; } ~PythonCallInDestructor() { d["good"] = true; }
@ -89,8 +90,6 @@ struct PythonCallInDestructor {
py::dict d; py::dict d;
}; };
struct PythonAlreadySetInDestructor { struct PythonAlreadySetInDestructor {
explicit PythonAlreadySetInDestructor(const py::str &s) : s(s) {} explicit PythonAlreadySetInDestructor(const py::str &s) : s(s) {}
~PythonAlreadySetInDestructor() { ~PythonAlreadySetInDestructor() {
@ -98,8 +97,7 @@ struct PythonAlreadySetInDestructor {
try { try {
// Assign to a py::object to force read access of nonexistent dict entry // Assign to a py::object to force read access of nonexistent dict entry
py::object o = foo["bar"]; py::object o = foo["bar"];
} } catch (py::error_already_set &ex) {
catch (py::error_already_set& ex) {
ex.discard_as_unraisable(s); ex.discard_as_unraisable(s);
} }
} }
@ -108,9 +106,8 @@ struct PythonAlreadySetInDestructor {
}; };
TEST_SUBMODULE(exceptions, m) { TEST_SUBMODULE(exceptions, m) {
m.def("throw_std_exception", []() { m.def("throw_std_exception",
throw std::runtime_error("This exception was intentionally thrown."); []() { throw std::runtime_error("This exception was intentionally thrown."); });
});
// make a new custom exception and use it as a translation target // make a new custom exception and use it as a translation target
static py::exception<MyException> ex(m, "MyException"); static py::exception<MyException> ex(m, "MyException");
@ -157,26 +154,30 @@ TEST_SUBMODULE(exceptions, m) {
// A slightly more complicated one that declares MyException5_1 as a subclass of MyException5 // 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_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) { py::register_local_exception_translator([](std::exception_ptr p) {
try { try {
if (p) { if (p) {
std::rethrow_exception(p); std::rethrow_exception(p);
} }
} catch (const MyException6 &e) { } catch (const MyException6 &e) {
PyErr_SetString(PyExc_RuntimeError, e.what()); PyErr_SetString(PyExc_RuntimeError, e.what());
} }
}); });
m.def("throws1", []() { throw MyException("this error should go to a custom type"); }); 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("throws3", []() { throw MyException3("this error cannot be translated"); });
m.def("throws4", []() { throw MyException4("this error is rethrown"); }); 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("throws5_1", []() { throw MyException5_1("MyException5 subclass"); });
m.def("throws6", []() { throw MyException6("MyException6 only handled in this module"); }); 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_overflow_error", []() { throw std::overflow_error(""); });
m.def("throws_local_error", []() { throw LocalException("never caught"); }); m.def("throws_local_error", []() { throw LocalException("never caught"); });
m.def("throws_local_simple_error", []() { throw LocalSimpleException("this mod"); }); m.def("throws_local_simple_error", []() { throw LocalSimpleException("this mod"); });
@ -185,8 +186,7 @@ TEST_SUBMODULE(exceptions, m) {
try { try {
// Assign to a py::object to force read access of nonexistent dict entry // Assign to a py::object to force read access of nonexistent dict entry
py::object o = foo["bar"]; py::object o = foo["bar"];
} } catch (py::error_already_set &ex) {
catch (py::error_already_set& ex) {
if (!ex.matches(PyExc_KeyError)) { if (!ex.matches(PyExc_KeyError)) {
throw; throw;
} }
@ -199,8 +199,7 @@ TEST_SUBMODULE(exceptions, m) {
try { try {
// Assign to a py::object to force read access of nonexistent dict entry // Assign to a py::object to force read access of nonexistent dict entry
py::object o = foo["bar"]; py::object o = foo["bar"];
} } catch (py::error_already_set &ex) {
catch (py::error_already_set &ex) {
if (!ex.matches(PyExc_Exception)) { if (!ex.matches(PyExc_Exception)) {
throw; throw;
} }
@ -212,8 +211,7 @@ TEST_SUBMODULE(exceptions, m) {
try { try {
// On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError // On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
py::module_::import("nonexistent"); py::module_::import("nonexistent");
} } catch (py::error_already_set &ex) {
catch (py::error_already_set &ex) {
if (!ex.matches(PyExc_ImportError)) { if (!ex.matches(PyExc_ImportError)) {
throw; throw;
} }
@ -228,10 +226,9 @@ TEST_SUBMODULE(exceptions, m) {
} }
try { try {
throw py::error_already_set(); throw py::error_already_set();
} catch (const std::runtime_error& e) { } catch (const std::runtime_error &e) {
if ((err && e.what() != std::string("ValueError: foo")) || if ((err && e.what() != std::string("ValueError: foo"))
(!err && e.what() != std::string("Unknown internal error occurred"))) || (!err && e.what() != std::string("Unknown internal error occurred"))) {
{
PyErr_Clear(); PyErr_Clear();
throw std::runtime_error("error message mismatch"); throw std::runtime_error("error message mismatch");
} }
@ -249,7 +246,7 @@ TEST_SUBMODULE(exceptions, m) {
PythonCallInDestructor set_dict_in_destructor(d); PythonCallInDestructor set_dict_in_destructor(d);
PyErr_SetString(PyExc_ValueError, "foo"); PyErr_SetString(PyExc_ValueError, "foo");
throw py::error_already_set(); throw py::error_already_set();
} catch (const py::error_already_set&) { } catch (const py::error_already_set &) {
retval = true; retval = true;
} }
return retval; return retval;
@ -275,7 +272,7 @@ TEST_SUBMODULE(exceptions, m) {
}); });
// Test repr that cannot be displayed // 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(); }); m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); });
@ -291,7 +288,7 @@ TEST_SUBMODULE(exceptions, m) {
try { try {
PyErr_SetString(PyExc_ValueError, "inner"); PyErr_SetString(PyExc_ValueError, "inner");
throw py::error_already_set(); throw py::error_already_set();
} catch (py::error_already_set& e) { } catch (py::error_already_set &e) {
py::raise_from(e, PyExc_ValueError, "outer"); py::raise_from(e, PyExc_ValueError, "outer");
throw py::error_already_set(); throw py::error_already_set();
} }

1
tests/test_exceptions.h

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

83
tests/test_factory_constructors.cpp

@ -10,6 +10,7 @@
#include "constructor_stats.h" #include "constructor_stats.h"
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <cmath> #include <cmath>
#include <new> #include <new>
#include <utility> #include <utility>
@ -87,6 +88,7 @@ class TestFactory6 {
protected: protected:
int value; int value;
bool alias = false; bool alias = false;
public: public:
explicit TestFactory6(int i) : value{i} { print_created(this, i); } explicit TestFactory6(int i) : value{i} { print_created(this, i); }
TestFactory6(TestFactory6 &&f) noexcept { TestFactory6(TestFactory6 &&f) noexcept {
@ -133,6 +135,7 @@ class TestFactory7 {
protected: protected:
int value; int value;
bool alias = false; bool alias = false;
public: public:
explicit TestFactory7(int i) : value{i} { print_created(this, i); } explicit TestFactory7(int i) : value{i} { print_created(this, i); }
TestFactory7(TestFactory7 &&f) noexcept { TestFactory7(TestFactory7 &&f) noexcept {
@ -166,14 +169,15 @@ public:
int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); } int get() override { PYBIND11_OVERRIDE(int, TestFactory7, get, /*no args*/); }
}; };
class TestFactoryHelper { class TestFactoryHelper {
public: public:
// Non-movable, non-copyable type: // Non-movable, non-copyable type:
// Return via pointer: // Return via pointer:
static TestFactory1 *construct1() { return new TestFactory1(); } static TestFactory1 *construct1() { return new TestFactory1(); }
// Holder: // 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 // pointer again
static TestFactory1 *construct1_string(std::string a) { static TestFactory1 *construct1_string(std::string a) {
return new TestFactory1(std::move(a)); return new TestFactory1(std::move(a));
@ -183,7 +187,9 @@ public:
// pointer: // pointer:
static TestFactory2 *construct2() { return new TestFactory2(); } static TestFactory2 *construct2() { return new TestFactory2(); }
// holder: // 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: // by value moving:
static TestFactory2 construct2(std::string a) { return TestFactory2(std::move(a)); } static TestFactory2 construct2(std::string a) { return TestFactory2(std::move(a)); }
@ -191,16 +197,18 @@ public:
// pointer: // pointer:
static TestFactory3 *construct3() { return new TestFactory3(); } static TestFactory3 *construct3() { return new TestFactory3(); }
// holder: // 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) { TEST_SUBMODULE(factory_constructors, m) {
// Define various trivial types to allow simpler overload resolution: // Define various trivial types to allow simpler overload resolution:
py::module_ m_tag = m.def_submodule("tag"); py::module_ m_tag = m.def_submodule("tag");
#define MAKE_TAG_TYPE(Name) \ #define MAKE_TAG_TYPE(Name) \
struct Name##_tag {}; \ struct Name##_tag {}; \
py::class_<Name##_tag>(m_tag, #Name "_tag").def(py::init<>()); \ py::class_<Name##_tag>(m_tag, #Name "_tag").def(py::init<>()); \
m_tag.attr(#Name) = py::cast(Name##_tag{}) m_tag.attr(#Name) = py::cast(Name##_tag{})
MAKE_TAG_TYPE(pointer); MAKE_TAG_TYPE(pointer);
MAKE_TAG_TYPE(unique_ptr); MAKE_TAG_TYPE(unique_ptr);
@ -223,9 +231,9 @@ TEST_SUBMODULE(factory_constructors, m) {
.def(py::init([](unique_ptr_tag, int v) { return TestFactoryHelper::construct1(v); })) .def(py::init([](unique_ptr_tag, int v) { return TestFactoryHelper::construct1(v); }))
.def(py::init(&TestFactoryHelper::construct1_string)) // raw function pointer .def(py::init(&TestFactoryHelper::construct1_string)) // raw function pointer
.def(py::init([](pointer_tag) { return TestFactoryHelper::construct1(); })) .def(py::init([](pointer_tag) { return TestFactoryHelper::construct1(); }))
.def(py::init([](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); })) .def(py::init(
.def_readwrite("value", &TestFactory1::value) [](py::handle, int v, py::handle) { return TestFactoryHelper::construct1(v); }))
; .def_readwrite("value", &TestFactory1::value);
py::class_<TestFactory2>(m, "TestFactory2") py::class_<TestFactory2>(m, "TestFactory2")
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); })) .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct2(v); }))
.def(py::init([](unique_ptr_tag, std::string v) { .def(py::init([](unique_ptr_tag, std::string v) {
@ -236,7 +244,10 @@ TEST_SUBMODULE(factory_constructors, m) {
// Stateful & reused: // Stateful & reused:
int c = 1; 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 // test_init_factory_basic, test_init_factory_casting
py::class_<TestFactory3, std::shared_ptr<TestFactory3>> pyTestFactory3(m, "TestFactory3"); py::class_<TestFactory3, std::shared_ptr<TestFactory3>> pyTestFactory3(m, "TestFactory3");
@ -253,16 +264,17 @@ TEST_SUBMODULE(factory_constructors, m) {
.def(py::init(c4a)) // derived ptr .def(py::init(c4a)) // derived ptr
.def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); })) .def(py::init([](pointer_tag, TF5_tag, int a) { return new TestFactory5(a); }))
// derived shared ptr: // derived shared ptr:
.def(py::init([](shared_ptr_tag, TF4_tag, int a) { return std::make_shared<TestFactory4>(a); })) .def(py::init(
.def(py::init([](shared_ptr_tag, TF5_tag, int a) { return std::make_shared<TestFactory5>(a); })) [](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: // Returns nullptr:
.def(py::init([](null_ptr_tag) { return (TestFactory3 *) 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_unique_ptr_tag) { return std::unique_ptr<TestFactory3>(); }))
.def(py::init([](null_shared_ptr_tag) { return std::shared_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 // test_init_factory_casting
py::class_<TestFactory4, TestFactory3, std::shared_ptr<TestFactory4>>(m, "TestFactory4") py::class_<TestFactory4, TestFactory3, std::shared_ptr<TestFactory4>>(m, "TestFactory4")
@ -346,9 +358,7 @@ TEST_SUBMODULE(factory_constructors, m) {
py::class_<NoPlacementNew>(m, "NoPlacementNew") py::class_<NoPlacementNew>(m, "NoPlacementNew")
.def(py::init<int>()) .def(py::init<int>())
.def(py::init([]() { return new NoPlacementNew(100); })) .def(py::init([]() { return new NoPlacementNew(100); }))
.def_readwrite("i", &NoPlacementNew::i) .def_readwrite("i", &NoPlacementNew::i);
;
// test_reallocations // test_reallocations
// Class that has verbose operator_new/operator_delete calls // Class that has verbose operator_new/operator_delete calls
@ -358,23 +368,36 @@ TEST_SUBMODULE(factory_constructors, m) {
explicit NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); } explicit NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); }
~NoisyAlloc() { py::print("~NoisyAlloc()"); } ~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 s) {
static void *operator new(size_t, void *p) { py::print("noisy placement new"); return p; } py::print("noisy new");
static void operator delete(void *p, size_t) { py::print("noisy delete"); ::operator delete(p); } 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"); } static void operator delete(void *, void *) { py::print("noisy placement delete"); }
#if defined(_MSC_VER) && _MSC_VER < 1910 #if defined(_MSC_VER) && _MSC_VER < 1910
// MSVC 2015 bug: the above "noisy delete" isn't invoked (fixed in MSVC 2017) // 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 #endif
}; };
py::class_<NoisyAlloc> pyNoisyAlloc(m, "NoisyAlloc"); py::class_<NoisyAlloc> pyNoisyAlloc(m, "NoisyAlloc");
// Since these overloads have the same number of arguments, the dispatcher will try each of // 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 // them until the arguments convert. Thus we can get a pre-allocation here when passing a
// single non-integer: // single non-integer:
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { 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); })); pyNoisyAlloc.def(py::init([](double d) { return new NoisyAlloc(d); }));
@ -385,7 +408,8 @@ TEST_SUBMODULE(factory_constructors, m) {
pyNoisyAlloc.def(py::init([](double d, int) { return NoisyAlloc(d); })); pyNoisyAlloc.def(py::init([](double d, int) { return NoisyAlloc(d); }));
// Old-style placement new init; requires preallocation // Old-style placement new init; requires preallocation
ignoreOldStyleInitWarnings([&pyNoisyAlloc]() { 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: // Requires deallocation of previous overload preallocated value:
pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); })); pyNoisyAlloc.def(py::init([](int i, double) { return new NoisyAlloc(i); }));
@ -395,7 +419,8 @@ TEST_SUBMODULE(factory_constructors, m) {
"__init__", [](NoisyAlloc &a, int i, const std::string &) { new (&a) NoisyAlloc(i); }); "__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 #if 0
struct BadF1Base {}; struct BadF1Base {};
struct BadF1 : BadF1Base {}; struct BadF1 : BadF1Base {};

44
tests/test_gil_scoped.cpp

@ -7,43 +7,41 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/functional.h> #include <pybind11/functional.h>
#include "pybind11_tests.h"
class VirtClass { class VirtClass {
public: public:
virtual ~VirtClass() = default; virtual ~VirtClass() = default;
VirtClass() = default; VirtClass() = default;
VirtClass(const VirtClass&) = delete; VirtClass(const VirtClass &) = delete;
virtual void virtual_func() {} virtual void virtual_func() {}
virtual void pure_virtual_func() = 0; virtual void pure_virtual_func() = 0;
}; };
class PyVirtClass : public VirtClass { class PyVirtClass : public VirtClass {
void virtual_func() override { void virtual_func() override { PYBIND11_OVERRIDE(void, VirtClass, virtual_func, ); }
PYBIND11_OVERRIDE(void, VirtClass, virtual_func,);
}
void pure_virtual_func() override { 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) { TEST_SUBMODULE(gil_scoped, m) {
py::class_<VirtClass, PyVirtClass>(m, "VirtClass") py::class_<VirtClass, PyVirtClass>(m, "VirtClass")
.def(py::init<>()) .def(py::init<>())
.def("virtual_func", &VirtClass::virtual_func) .def("virtual_func", &VirtClass::virtual_func)
.def("pure_virtual_func", &VirtClass::pure_virtual_func); .def("pure_virtual_func", &VirtClass::pure_virtual_func);
m.def("test_callback_py_obj", [](py::object &func) { 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_std_func", [](const std::function<void()> &func) { func(); });
m.def("test_callback_virtual_func", [](VirtClass &virt) { virt.virtual_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_callback_pure_virtual_func", [](VirtClass &virt) { virt.pure_virtual_func(); });
m.def("test_cross_module_gil", []() { m.def("test_cross_module_gil", []() {
auto cm = py::module_::import("cross_module_gil_utils"); auto cm = py::module_::import("cross_module_gil_utils");
auto gil_acquire auto gil_acquire = reinterpret_cast<void (*)()>(
= reinterpret_cast<void (*)()>(PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr())); PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr()));
py::gil_scoped_release gil_release; py::gil_scoped_release gil_release;
gil_acquire(); gil_acquire();
}); });
} }

30
tests/test_iostream.cpp

@ -7,12 +7,14 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC #if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC
# pragma warning(disable: 4702) // unreachable code in system header (xatomic.h(382)) # pragma warning(disable : 4702) // unreachable code in system header (xatomic.h(382))
#endif #endif
#include <pybind11/iostream.h> #include <pybind11/iostream.h>
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <atomic> #include <atomic>
#include <iostream> #include <iostream>
#include <mutex> #include <mutex>
@ -51,13 +53,12 @@ struct TestThread {
std::cout << "x" << std::flush; std::cout << "x" << std::flush;
} }
std::this_thread::sleep_for(std::chrono::microseconds(50)); std::this_thread::sleep_for(std::chrono::microseconds(50));
} }; }
};
t_ = new std::thread(std::move(thread_f)); t_ = new std::thread(std::move(thread_f));
} }
~TestThread() { ~TestThread() { delete t_; }
delete t_;
}
void stop() { stop_ = true; } void stop() { stop_ = true; }
@ -75,7 +76,6 @@ struct TestThread {
std::atomic<bool> stop_; std::atomic<bool> stop_;
}; };
TEST_SUBMODULE(iostream, m) { TEST_SUBMODULE(iostream, m) {
add_ostream_redirect(m); add_ostream_redirect(m);
@ -92,9 +92,11 @@ TEST_SUBMODULE(iostream, m) {
std::cout << msg << std::flush; std::cout << msg << std::flush;
}); });
m.def("guard_output", &noisy_function, m.def("guard_output",
py::call_guard<py::scoped_ostream_redirect>(), &noisy_function,
py::arg("msg"), py::arg("flush")=true); py::call_guard<py::scoped_ostream_redirect>(),
py::arg("msg"),
py::arg("flush") = true);
m.def("captured_err", [](const std::string &msg) { m.def("captured_err", [](const std::string &msg) {
py::scoped_ostream_redirect redir(std::cerr, py::module_::import("sys").attr("stderr")); py::scoped_ostream_redirect redir(std::cerr, py::module_::import("sys").attr("stderr"));
@ -103,9 +105,11 @@ TEST_SUBMODULE(iostream, m) {
m.def("noisy_function", &noisy_function, py::arg("msg"), py::arg("flush") = true); m.def("noisy_function", &noisy_function, py::arg("msg"), py::arg("flush") = true);
m.def("dual_guard", &noisy_funct_dual, m.def("dual_guard",
py::call_guard<py::scoped_ostream_redirect, py::scoped_estream_redirect>(), &noisy_funct_dual,
py::arg("msg"), py::arg("emsg")); 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; }); m.def("raw_output", [](const std::string &msg) { std::cout << msg << std::flush; });

241
tests/test_kwargs_and_defaults.cpp

@ -7,39 +7,43 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
#include <utility> #include <utility>
TEST_SUBMODULE(kwargs_and_defaults, m) { 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 // test_named_arguments
m.def("kw_func0", kw_func); m.def("kw_func0", kw_func);
m.def("kw_func1", kw_func, py::arg("x"), py::arg("y")); 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_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 */ /* A fancier default argument */
std::vector<int> list{{13, 17}}; std::vector<int> list{{13, 17}};
m.def("kw_func4", [](const std::vector<int> &entries) { m.def(
std::string ret = "{"; "kw_func4",
for (int i : entries) { [](const std::vector<int> &entries) {
ret += std::to_string(i) + " "; std::string ret = "{";
} for (int i : entries) {
ret.back() = '}'; ret += std::to_string(i) + " ";
return ret; }
}, py::arg("myList") = list); 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", 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_z", kw_func, "x"_a, "y"_a = 0);
// test_args_and_kwargs // test_args_and_kwargs
m.def("args_function", [](py::args args) -> py::tuple { m.def("args_function", [](py::args args) -> py::tuple { return std::move(args); });
return std::move(args);
});
m.def("args_kwargs_function", [](const py::args &args, const py::kwargs &kwargs) { m.def("args_kwargs_function", [](const py::args &args, const py::kwargs &kwargs) {
return py::make_tuple(args, kwargs); return py::make_tuple(args, kwargs);
}); });
@ -54,35 +58,60 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
}; };
m.def("mixed_plus_args_kwargs", mixed_plus_both); m.def("mixed_plus_args_kwargs", mixed_plus_both);
m.def("mixed_plus_args_kwargs_defaults", mixed_plus_both, m.def("mixed_plus_args_kwargs_defaults",
py::arg("i") = 1, py::arg("j") = 3.14159); mixed_plus_both,
py::arg("i") = 1,
m.def("args_kwonly", py::arg("j") = 3.14159);
[](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(
m.def("args_kwonly_kwargs", "args_kwonly",
[](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) { [](int i, double j, const py::args &args, int z) { return py::make_tuple(i, j, args, z); },
return py::make_tuple(i, j, args, z, kwargs); }, "i"_a,
"i"_a, "j"_a, py::kw_only{}, "z"_a); "j"_a,
m.def("args_kwonly_kwargs_defaults", "z"_a);
[](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) { m.def(
return py::make_tuple(i, j, args, z, kwargs); }, "args_kwonly_kwargs",
"i"_a = 1, "j"_a = 3.14159, "z"_a = 42); [](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
m.def("args_kwonly_full_monty", return py::make_tuple(i, j, args, z, kwargs);
[](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); }, "i"_a,
py::arg() = 1, py::arg() = 2, py::pos_only{}, "j"_a = 3.14159, "z"_a = 42); "j"_a,
py::kw_only{},
"z"_a);
// test_args_refcount m.def(
// PyPy needs a garbage collection to get the reference count values to match CPython's behaviour "args_kwonly_kwargs_defaults",
#ifdef PYPY_VERSION [](int i, double j, const py::args &args, int z, const py::kwargs &kwargs) {
#define GC_IF_NEEDED ConstructorStats::gc() return py::make_tuple(i, j, args, z, kwargs);
#else },
#define GC_IF_NEEDED "i"_a = 1,
#endif "j"_a = 3.14159,
m.def("arg_refcount_h", [](py::handle h) { GC_IF_NEEDED; return h.ref_count(); }); "z"_a = 42);
m.def("arg_refcount_h", [](py::handle h, py::handle, py::handle) { GC_IF_NEEDED; return h.ref_count(); }); 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) { m.def("arg_refcount_o", [](const py::object &o) {
GC_IF_NEEDED; GC_IF_NEEDED;
return o.ref_count(); return o.ref_count();
@ -109,23 +138,42 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
// pybind11 won't allow these to be bound: args and kwargs, if present, must be at the end. // 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: // Uncomment these to test that the static_assert is indeed working:
// m.def("bad_args1", [](py::args, int) {}); // m.def("bad_args1", [](py::args, int) {});
// m.def("bad_args2", [](py::kwargs, int) {}); // m.def("bad_args2", [](py::kwargs, int) {});
// m.def("bad_args3", [](py::kwargs, py::args) {}); // m.def("bad_args3", [](py::kwargs, py::args) {});
// m.def("bad_args4", [](py::args, int, py::kwargs) {}); // m.def("bad_args4", [](py::args, int, py::kwargs) {});
// m.def("bad_args5", [](py::args, py::kwargs, int) {}); // m.def("bad_args5", [](py::args, py::kwargs, int) {});
// m.def("bad_args6", [](py::args, py::args) {}); // m.def("bad_args6", [](py::args, py::args) {});
// m.def("bad_args7", [](py::kwargs, py::kwargs) {}); // m.def("bad_args7", [](py::kwargs, py::kwargs) {});
// test_keyword_only_args // test_keyword_only_args
m.def("kw_only_all", [](int i, int j) { return py::make_tuple(i, j); }, m.def(
py::kw_only(), py::arg("i"), py::arg("j")); "kw_only_all",
m.def("kw_only_some", [](int i, int j, int k) { return py::make_tuple(i, j, k); }, [](int i, int j) { return py::make_tuple(i, j); },
py::arg(), py::kw_only(), py::arg("j"), py::arg("k")); py::kw_only(),
m.def("kw_only_with_defaults", [](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); }, py::arg("i"),
py::arg() = 3, "j"_a = 4, py::kw_only(), "k"_a = 5, "z"_a); py::arg("j"));
m.def("kw_only_mixed", [](int i, int j) { return py::make_tuple(i, j); }, m.def(
"i"_a, py::kw_only(), "j"_a); "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( m.def(
"kw_only_plus_more", "kw_only_plus_more",
[](int i, int j, int k, const py::kwargs &kwargs) { [](int i, int j, int k, const py::kwargs &kwargs) {
@ -137,31 +185,57 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
py::arg("k") /* kw-only */); py::arg("k") /* kw-only */);
m.def("register_invalid_kw_only", [](py::module_ m) { m.def("register_invalid_kw_only", [](py::module_ m) {
m.def("bad_kw_only", [](int i, int j) { return py::make_tuple(i, j); }, m.def(
py::kw_only(), py::arg() /* invalid unnamed argument */, "j"_a); "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 // test_positional_only_args
m.def("pos_only_all", [](int i, int j) { return py::make_tuple(i, j); }, m.def(
py::arg("i"), py::arg("j"), py::pos_only()); "pos_only_all",
m.def("pos_only_mix", [](int i, int j) { return py::make_tuple(i, j); }, [](int i, int j) { return py::make_tuple(i, j); },
py::arg("i"), py::pos_only(), py::arg("j")); py::arg("i"),
m.def("pos_kw_only_mix", [](int i, int j, int k) { return py::make_tuple(i, j, k); }, py::arg("j"),
py::arg("i"), py::pos_only(), py::arg("j"), py::kw_only(), py::arg("k")); py::pos_only());
m.def("pos_only_def_mix", [](int i, int j, int k) { return py::make_tuple(i, j, k); }, m.def(
py::arg("i"), py::arg("j") = 2, py::pos_only(), py::arg("k") = 3); "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: // These should fail to compile:
#ifdef PYBIND11_NEVER_DEFINED_EVER #ifdef PYBIND11_NEVER_DEFINED_EVER
// argument annotations are required when using kw_only // 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 // 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 #endif
// test_function_signatures (along with most of the above) // 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") py::class_<KWClass>(m, "KWClass")
.def("foo0", &KWClass::foo) .def("foo0", &KWClass::foo)
.def("foo1", &KWClass::foo, "x"_a, "y"_a); .def("foo1", &KWClass::foo, "x"_a, "y"_a);
@ -182,11 +256,18 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
.def(py::init([](int) { return first_arg_kw_only(); }), .def(py::init([](int) { return first_arg_kw_only(); }),
py::kw_only(), // This being before any args was broken py::kw_only(), // This being before any args was broken
py::arg("i") = 0) py::arg("i") = 0)
.def("method", [](first_arg_kw_only&, int, int) {}, .def(
py::kw_only(), // and likewise here "method",
py::arg("i") = 1, py::arg("j") = 2) [](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 // Closely related: pos_only marker didn't show up properly when it was before any other
// arguments (although that is fairly useless in practice). // arguments (although that is fairly useless in practice).
.def("pos_only", [](first_arg_kw_only&, int, int) {}, .def(
py::pos_only{}, py::arg("i"), py::arg("j")); "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 @@
BSD-style license that can be found in the LICENSE file. 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.h>
#include <pybind11/stl_bind.h> #include <pybind11/stl_bind.h>
#include "local_bindings.h"
#include "pybind11_tests.h"
#include <numeric> #include <numeric>
#include <utility> #include <utility>
@ -24,9 +24,9 @@ TEST_SUBMODULE(local_bindings, m) {
// test_local_bindings // test_local_bindings
// Register a class with py::module_local: // Register a class with py::module_local:
bind_local<LocalType, -1>(m, "LocalType", py::module_local()) bind_local<LocalType, -1>(m, "LocalType", py::module_local()).def("get3", [](LocalType &t) {
.def("get3", [](LocalType &t) { return t.i + 3; }) return t.i + 3;
; });
m.def("local_value", [](LocalType &l) { return l.i; }); m.def("local_value", [](LocalType &l) { return l.i; });
@ -35,14 +35,14 @@ TEST_SUBMODULE(local_bindings, m) {
// one, in pybind11_cross_module_tests.cpp, is designed to fail): // one, in pybind11_cross_module_tests.cpp, is designed to fail):
bind_local<NonLocalType, 0>(m, "NonLocalType") bind_local<NonLocalType, 0>(m, "NonLocalType")
.def(py::init<int>()) .def(py::init<int>())
.def("get", [](LocalType &i) { return i.i; }) .def("get", [](LocalType &i) { return i.i; });
;
// test_duplicate_local // test_duplicate_local
// py::module_local declarations should be visible across compilation units that get linked together; // py::module_local declarations should be visible across compilation units that get linked
// this tries to register a duplicate local. It depends on a definition in test_class.cpp and // together; this tries to register a duplicate local. It depends on a definition in
// should raise a runtime error from the duplicate definition attempt. If test_class isn't // test_class.cpp and should raise a runtime error from the duplicate definition attempt. If
// available it *also* throws a runtime error (with "test_class not enabled" as value). // test_class isn't available it *also* throws a runtime error (with "test_class not enabled"
// as value).
m.def("register_local_external", [m]() { m.def("register_local_external", [m]() {
auto main = py::module_::import("pybind11_tests"); auto main = py::module_::import("pybind11_tests");
if (py::hasattr(main, "class_")) { if (py::hasattr(main, "class_")) {
@ -79,12 +79,12 @@ TEST_SUBMODULE(local_bindings, m) {
m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); }); m.def("get_mixed_lg", [](int i) { return MixedLocalGlobal(i); });
// test_internal_locals_differ // 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 // test_stl_caster_vs_stl_bind
m.def("load_vector_via_caster", [](std::vector<int> v) { m.def("load_vector_via_caster",
return std::accumulate(v.begin(), v.end(), 0); [](std::vector<int> v) { return std::accumulate(v.begin(), v.end(), 0); });
});
// test_cross_module_calls // test_cross_module_calls
m.def("return_self", [](LocalVec *v) { return v; }); m.def("return_self", [](LocalVec *v) { return v; });
@ -94,11 +94,9 @@ TEST_SUBMODULE(local_bindings, m) {
public: public:
explicit Cat(std::string name) : Pet(std::move(name)) {} explicit Cat(std::string name) : Pet(std::move(name)) {}
}; };
py::class_<pets::Pet>(m, "Pet", py::module_local()) py::class_<pets::Pet>(m, "Pet", py::module_local()).def("get_name", &pets::Pet::name);
.def("get_name", &pets::Pet::name);
// Binding for local extending class: // Binding for local extending class:
py::class_<Cat, pets::Pet>(m, "Cat") py::class_<Cat, pets::Pet>(m, "Cat").def(py::init<std::string>());
.def(py::init<std::string>());
m.def("pet_name", [](pets::Pet &p) { return p.name(); }); m.def("pet_name", [](pets::Pet &p) { return p.name(); });
py::class_<MixGL>(m, "MixGL").def(py::init<int>()); py::class_<MixGL>(m, "MixGL").def(py::init<int>());

194
tests/test_methods_and_attributes.cpp

@ -8,8 +8,8 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
#include "pybind11_tests.h"
#if !defined(PYBIND11_OVERLOAD_CAST) #if !defined(PYBIND11_OVERLOAD_CAST)
template <typename... Args> template <typename... Args>
@ -27,7 +27,10 @@ public:
std::string toString() const { return "ExampleMandA[value=" + std::to_string(value) + "]"; } 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 { void operator=(ExampleMandA &&e) noexcept {
print_move_assigned(this); print_move_assigned(this);
value = e.value; value = e.value;
@ -40,37 +43,37 @@ public:
void add4(ExampleMandA *other) { value += other->value; } // passing by pointer void add4(ExampleMandA *other) { value += other->value; } // passing by pointer
void add5(const ExampleMandA *other) { value += other->value; } // passing by const pointer void add5(const ExampleMandA *other) { value += other->value; } // passing by const pointer
void add6(int other) { value += other; } // passing by value void add6(int other) { value += other; } // passing by value
void add7(int &other) { value += other; } // passing by reference void add7(int &other) { value += other; } // passing by reference
void add8(const int &other) { value += other; } // passing by const reference void add8(const int &other) { value += other; } // passing by const reference
// NOLINTNEXTLINE(readability-non-const-parameter) Deliberately non-const for testing // NOLINTNEXTLINE(readability-non-const-parameter) Deliberately non-const for testing
void add9(int *other) { value += *other; } // passing by pointer void add9(int *other) { value += *other; } // passing by pointer
void add10(const int *other) { value += *other; } // passing by const pointer void add10(const int *other) { value += *other; } // passing by const pointer
void consume_str(std::string&&) {} void consume_str(std::string &&) {}
ExampleMandA self1() { return *this; } // return by value ExampleMandA self1() { return *this; } // return by value
ExampleMandA &self2() { return *this; } // return by reference ExampleMandA &self2() { return *this; } // return by reference
const ExampleMandA &self3() const { return *this; } // return by const reference const ExampleMandA &self3() const { return *this; } // return by const reference
ExampleMandA *self4() { return this; } // return by pointer ExampleMandA *self4() { return this; } // return by pointer
const ExampleMandA *self5() const { return this; } // return by const pointer const ExampleMandA *self5() const { return this; } // return by const pointer
int internal1() const { return value; } // return by value int internal1() const { return value; } // return by value
int &internal2() { return value; } // return by reference int &internal2() { return value; } // return by reference
const int &internal3() const { return value; } // return by const reference const int &internal3() const { return value; } // return by const reference
int *internal4() { return &value; } // return by pointer int *internal4() { return &value; } // return by pointer
const int *internal5() { return &value; } // return by const pointer const int *internal5() { return &value; } // return by const pointer
py::str overloaded() { return "()"; } py::str overloaded() { return "()"; }
py::str overloaded(int) { return "(int)"; } py::str overloaded(int) { return "(int)"; }
py::str overloaded(int, float) { return "(int, float)"; } py::str overloaded(int, float) { return "(int, float)"; }
py::str overloaded(float, int) { return "(float, int)"; } py::str overloaded(float, int) { return "(float, int)"; }
py::str overloaded(int, int) { return "(int, int)"; } py::str overloaded(int, int) { return "(int, int)"; }
py::str overloaded(float, float) { return "(float, float)"; } py::str overloaded(float, float) { return "(float, float)"; }
py::str overloaded(int) const { return "(int) const"; } py::str overloaded(int) const { return "(int) const"; }
py::str overloaded(int, float) const { return "(int, float) const"; } py::str overloaded(int, float) const { return "(int, float) const"; }
py::str overloaded(float, int) const { return "(float, int) 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, int) const { return "(int, int) const"; }
py::str overloaded(float, float) const { return "(float, float) const"; } py::str overloaded(float, float) const { return "(float, float) const"; }
static py::str overloaded(float) { return "static float"; } static py::str overloaded(float) { return "static float"; }
@ -112,7 +115,10 @@ UserType TestPropRVP::sv1(1);
UserType TestPropRVP::sv2(1); UserType TestPropRVP::sv2(1);
// Test None-allowed py::arg argument policy // 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 none1(const NoneTester &obj) { return obj.answer; }
int none2(NoneTester *obj) { return obj ? obj->answer : -1; } int none2(NoneTester *obj) { return obj ? obj->answer : -1; }
int none3(std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; } int none3(std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
@ -134,11 +140,15 @@ struct StrIssue {
explicit StrIssue(int i) : val{i} {} 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 { class UnregisteredBase {
public: public:
void do_nothing() const {} 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; } void set_int(int v) { rw_value = v; }
int get_int() const { return rw_value; } int get_int() const { return rw_value; }
double get_double() const { return ro_value; } double get_double() const { return ro_value; }
@ -161,10 +171,10 @@ struct RefQualified {
// Test rvalue ref param // Test rvalue ref param
struct RValueRefParam { struct RValueRefParam {
std::size_t func1(std::string&& s) { 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 func2(std::string &&s) const { return s.size(); }
std::size_t func3(std::string&& s) & { 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 func4(std::string &&s) const & { return s.size(); }
}; };
TEST_SUBMODULE(methods_and_attributes, m) { TEST_SUBMODULE(methods_and_attributes, m) {
@ -172,8 +182,8 @@ TEST_SUBMODULE(methods_and_attributes, m) {
py::class_<ExampleMandA> emna(m, "ExampleMandA"); py::class_<ExampleMandA> emna(m, "ExampleMandA");
emna.def(py::init<>()) emna.def(py::init<>())
.def(py::init<int>()) .def(py::init<int>())
.def(py::init<std::string&&>()) .def(py::init<std::string &&>())
.def(py::init<const ExampleMandA&>()) .def(py::init<const ExampleMandA &>())
.def("add1", &ExampleMandA::add1) .def("add1", &ExampleMandA::add1)
.def("add2", &ExampleMandA::add2) .def("add2", &ExampleMandA::add2)
.def("add3", &ExampleMandA::add3) .def("add3", &ExampleMandA::add3)
@ -198,16 +208,20 @@ TEST_SUBMODULE(methods_and_attributes, m) {
#if defined(PYBIND11_OVERLOAD_CAST) #if defined(PYBIND11_OVERLOAD_CAST)
.def("overloaded", py::overload_cast<>(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast<>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<int>(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast<int>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<int, float>(&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<float, int>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<int, int>(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast<int, int>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<float, float>(&ExampleMandA::overloaded)) .def("overloaded", py::overload_cast<float, float>(&ExampleMandA::overloaded))
.def("overloaded_float", 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>(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", py::overload_cast<int, float>(&ExampleMandA::overloaded, py::const_)) .def("overloaded_const",
.def("overloaded_const", py::overload_cast<float, int>(&ExampleMandA::overloaded, py::const_)) py::overload_cast<int, float>(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", py::overload_cast<int, int>(&ExampleMandA::overloaded, py::const_)) .def("overloaded_const",
.def("overloaded_const", py::overload_cast<float, float>(&ExampleMandA::overloaded, py::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 #else
// Use both the traditional static_cast method and the C++11 compatible overload_cast_ // Use both the traditional static_cast method and the C++11 compatible overload_cast_
.def("overloaded", overload_cast_<>()(&ExampleMandA::overloaded)) .def("overloaded", overload_cast_<>()(&ExampleMandA::overloaded))
@ -225,16 +239,29 @@ TEST_SUBMODULE(methods_and_attributes, m) {
#endif #endif
// test_no_mixed_overloads // test_no_mixed_overloads
// Raise error if trying to mix static/non-static overloads on the same name: // Raise error if trying to mix static/non-static overloads on the same name:
.def_static("add_mixed_overloads1", []() { .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)) auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(
.def_static("overload_mixed1", static_cast<py::str ( *)(float )>(&ExampleMandA::overloaded)); py::module_::import("pybind11_tests.methods_and_attributes")
}) .attr("ExampleMandA"));
.def_static("add_mixed_overloads2", []() { emna.def("overload_mixed1",
auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module_::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA")); static_cast<py::str (ExampleMandA::*)(int, int)>(
emna.def_static("overload_mixed2", static_cast<py::str ( *)(float )>(&ExampleMandA::overloaded)) &ExampleMandA::overloaded))
.def ("overload_mixed2", 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("__str__", &ExampleMandA::toString)
.def_readwrite("value", &ExampleMandA::value); .def_readwrite("value", &ExampleMandA::value);
@ -307,7 +334,7 @@ TEST_SUBMODULE(methods_and_attributes, m) {
[](const py::object &) { return UserType(1); }); [](const py::object &) { return UserType(1); });
// test_metaclass_override // test_metaclass_override
struct MetaclassOverride { }; struct MetaclassOverride {};
py::class_<MetaclassOverride>(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type)) py::class_<MetaclassOverride>(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type))
.def_property_readonly_static("readonly", [](const py::object &) { return 1; }); .def_property_readonly_static("readonly", [](const py::object &) { return 1; });
@ -315,22 +342,21 @@ TEST_SUBMODULE(methods_and_attributes, m) {
m.def("overload_order", [](const std::string &) { return 1; }); m.def("overload_order", [](const std::string &) { return 1; });
m.def("overload_order", [](const std::string &) { return 2; }); m.def("overload_order", [](const std::string &) { return 2; });
m.def("overload_order", [](int) { return 3; }); 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) #if !defined(PYPY_VERSION)
// test_dynamic_attributes // test_dynamic_attributes
class DynamicClass { class DynamicClass {
public: public:
DynamicClass() { print_default_created(this); } DynamicClass() { print_default_created(this); }
DynamicClass(const DynamicClass&) = delete; DynamicClass(const DynamicClass &) = delete;
~DynamicClass() { print_destroyed(this); } ~DynamicClass() { print_destroyed(this); }
}; };
py::class_<DynamicClass>(m, "DynamicClass", py::dynamic_attr()) py::class_<DynamicClass>(m, "DynamicClass", py::dynamic_attr()).def(py::init());
.def(py::init());
class CppDerivedDynamicClass : public DynamicClass { }; class CppDerivedDynamicClass : public DynamicClass {};
py::class_<CppDerivedDynamicClass, DynamicClass>(m, "CppDerivedDynamicClass") py::class_<CppDerivedDynamicClass, DynamicClass>(m, "CppDerivedDynamicClass").def(py::init());
.def(py::init());
#endif #endif
// test_bad_arg_default // test_bad_arg_default
@ -340,20 +366,27 @@ TEST_SUBMODULE(methods_and_attributes, m) {
#else #else
m.attr("debug_enabled") = false; m.attr("debug_enabled") = false;
#endif #endif
m.def("bad_arg_def_named", []{ m.def("bad_arg_def_named", [] {
auto m = py::module_::import("pybind11_tests"); 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"); 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. // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
// test_accepts_none // test_accepts_none
py::class_<NoneTester, std::shared_ptr<NoneTester>>(m, "NoneTester") py::class_<NoneTester, std::shared_ptr<NoneTester>>(m, "NoneTester").def(py::init<>());
.def(py::init<>());
m.def("no_none1", &none1, py::arg{}.none(false)); m.def("no_none1", &none1, py::arg{}.none(false));
m.def("no_none2", &none2, py::arg{}.none(false)); m.def("no_none2", &none2, py::arg{}.none(false));
m.def("no_none3", &none3, py::arg{}.none(false)); m.def("no_none3", &none3, py::arg{}.none(false));
@ -371,21 +404,19 @@ TEST_SUBMODULE(methods_and_attributes, m) {
// test_casts_none // test_casts_none
// Issue #2778: implicit casting from None to object (not pointer) // Issue #2778: implicit casting from None to object (not pointer)
py::class_<NoneCastTester>(m, "NoneCastTester") py::class_<NoneCastTester>(m, "NoneCastTester")
.def(py::init<>()) .def(py::init<>())
.def(py::init<int>()) .def(py::init<int>())
.def(py::init([](py::none const&) { return NoneCastTester{}; })); .def(py::init([](py::none const &) { return NoneCastTester{}; }));
py::implicitly_convertible<py::none, 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 // test_str_issue
// Issue #283: __str__ called on uninitialized instance when constructor arguments invalid // Issue #283: __str__ called on uninitialized instance when constructor arguments invalid
py::class_<StrIssue>(m, "StrIssue") py::class_<StrIssue>(m, "StrIssue")
.def(py::init<int>()) .def(py::init<int>())
.def(py::init<>()) .def(py::init<>())
.def("__str__", [](const StrIssue &si) { .def("__str__",
return "StrIssue[" + std::to_string(si.val) + "]"; } [](const StrIssue &si) { return "StrIssue[" + std::to_string(si.val) + "]"; });
);
// test_unregistered_base_implementations // test_unregistered_base_implementations
// //
@ -408,7 +439,8 @@ TEST_SUBMODULE(methods_and_attributes, m) {
// This one is in the registered class: // This one is in the registered class:
.def("sum", &RegisteredDerived::sum); .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, ""); static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, "");
// test_methods_and_attributes // test_methods_and_attributes

66
tests/test_modules.cpp

@ -8,8 +8,8 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
#include "pybind11_tests.h"
TEST_SUBMODULE(modules, m) { TEST_SUBMODULE(modules, m) {
// test_nested_modules // test_nested_modules
@ -22,23 +22,30 @@ TEST_SUBMODULE(modules, m) {
public: public:
explicit A(int v) : v(v) { print_created(this, v); } explicit A(int v) : v(v) { print_created(this, v); }
~A() { print_destroyed(this); } ~A() { print_destroyed(this); }
A(const A&) { print_copy_created(this); } A(const A &) { print_copy_created(this); }
A& operator=(const A &copy) { print_copy_assigned(this); v = copy.v; return *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) + "]"; } std::string toString() const { return "A[" + std::to_string(v) + "]"; }
private: private:
int v; int v;
}; };
py::class_<A>(m_sub, "A") py::class_<A>(m_sub, "A").def(py::init<int>()).def("__repr__", &A::toString);
.def(py::init<int>())
.def("__repr__", &A::toString);
class B { class B {
public: public:
B() { print_default_created(this); } B() { print_default_created(this); }
~B() { print_destroyed(this); } ~B() { print_destroyed(this); }
B(const B&) { print_copy_created(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 &operator=(const B &copy) {
print_copy_assigned(this);
a1 = copy.a1;
a2 = copy.a2;
return *this;
}
A &get_a1() { return a1; } A &get_a1() { return a1; }
A &get_a2() { return a2; } A &get_a2() { return a2; }
@ -47,10 +54,16 @@ TEST_SUBMODULE(modules, m) {
}; };
py::class_<B>(m_sub, "B") py::class_<B>(m_sub, "B")
.def(py::init<>()) .def(py::init<>())
.def("get_a1", &B::get_a1, "Return the internal A 1", py::return_value_policy::reference_internal) .def("get_a1",
.def("get_a2", &B::get_a2, "Return the internal A 2", py::return_value_policy::reference_internal) &B::get_a1,
.def_readwrite("a1", &B::a1) // def_readonly uses an internal "Return the internal A 1",
// reference return policy by default 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); .def_readwrite("a2", &B::a2);
// This is intentionally "py::module" to verify it still can be used in place of "py::module_" // This is intentionally "py::module" to verify it still can be used in place of "py::module_"
@ -59,13 +72,14 @@ TEST_SUBMODULE(modules, m) {
// test_duplicate_registration // test_duplicate_registration
// Registering two things with the same name // Registering two things with the same name
m.def("duplicate_registration", []() { m.def("duplicate_registration", []() {
class Dupe1 { }; class Dupe1 {};
class Dupe2 { }; class Dupe2 {};
class Dupe3 { }; class Dupe3 {};
class DupeException { }; class DupeException {};
// Go ahead and leak, until we have a non-leaking py::module_ constructor // 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(); auto failures = py::list();
py::class_<Dupe1>(dm, "Dupe1"); py::class_<Dupe1>(dm, "Dupe1");
@ -76,27 +90,33 @@ TEST_SUBMODULE(modules, m) {
try { try {
py::class_<Dupe1>(dm, "Dupe1"); py::class_<Dupe1>(dm, "Dupe1");
failures.append("Dupe1 class"); failures.append("Dupe1 class");
} catch (std::runtime_error &) {} } catch (std::runtime_error &) {
}
try { try {
dm.def("Dupe1", []() { return Dupe1(); }); dm.def("Dupe1", []() { return Dupe1(); });
failures.append("Dupe1 function"); failures.append("Dupe1 function");
} catch (std::runtime_error &) {} } catch (std::runtime_error &) {
}
try { try {
py::class_<Dupe3>(dm, "dupe1_factory"); py::class_<Dupe3>(dm, "dupe1_factory");
failures.append("dupe1_factory"); failures.append("dupe1_factory");
} catch (std::runtime_error &) {} } catch (std::runtime_error &) {
}
try { try {
py::exception<Dupe3>(dm, "Dupe2"); py::exception<Dupe3>(dm, "Dupe2");
failures.append("Dupe2"); failures.append("Dupe2");
} catch (std::runtime_error &) {} } catch (std::runtime_error &) {
}
try { try {
dm.def("DupeException", []() { return 30; }); dm.def("DupeException", []() { return 30; });
failures.append("DupeException1"); failures.append("DupeException1");
} catch (std::runtime_error &) {} } catch (std::runtime_error &) {
}
try { try {
py::class_<DupeException>(dm, "DupeException"); py::class_<DupeException>(dm, "DupeException");
failures.append("DupeException2"); failures.append("DupeException2");
} catch (std::runtime_error &) {} } catch (std::runtime_error &) {
}
return failures; return failures;
}); });

137
tests/test_multiple_inheritance.cpp

@ -8,14 +8,15 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h" #include "constructor_stats.h"
#include "pybind11_tests.h"
namespace { namespace {
// Many bases for testing that multiple inheritance from many classes (i.e. requiring extra // Many bases for testing that multiple inheritance from many classes (i.e. requiring extra
// space for holder constructed flags) works. // space for holder constructed flags) works.
template <int N> struct BaseN { template <int N>
struct BaseN {
explicit BaseN(int i) : i(i) {} explicit BaseN(int i) : i(i) {}
int i; int i;
}; };
@ -57,13 +58,23 @@ struct Base2a {
int i; int i;
}; };
struct Base12a : Base1a, Base2a { 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_unaligned_base
// test_mi_base_return // test_mi_base_return
struct I801B1 { int a = 1; I801B1() = default; I801B1(const I801B1 &) = default; virtual ~I801B1() = default; }; struct I801B1 {
struct I801B2 { int b = 2; I801B2() = default; I801B2(const I801B2 &) = default; virtual ~I801B2() = default; }; 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 I801C : I801B1, I801B2 {};
struct I801D : I801C {}; // Indirect MI struct I801D : I801C {}; // Indirect MI
@ -82,8 +93,7 @@ TEST_SUBMODULE(multiple_inheritance, m) {
int i; int i;
}; };
py::class_<Base1> b1(m, "Base1"); py::class_<Base1> b1(m, "Base1");
b1.def(py::init<int>()) b1.def(py::init<int>()).def("foo", &Base1::foo);
.def("foo", &Base1::foo);
struct Base2 { struct Base2 {
explicit Base2(int i) : i(i) {} explicit Base2(int i) : i(i) {}
@ -91,42 +101,49 @@ TEST_SUBMODULE(multiple_inheritance, m) {
int i; int i;
}; };
py::class_<Base2> b2(m, "Base2"); py::class_<Base2> b2(m, "Base2");
b2.def(py::init<int>()) b2.def(py::init<int>()).def("bar", &Base2::bar);
.def("bar", &Base2::bar);
// test_multiple_inheritance_cpp // test_multiple_inheritance_cpp
struct Base12 : Base1, Base2 { 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 { 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_<Base12, Base1, Base2>(m, "Base12");
py::class_<MIType, Base12>(m, "MIType") py::class_<MIType, Base12>(m, "MIType").def(py::init<int, int>());
.def(py::init<int, int>());
// test_multiple_inheritance_python_many_bases // test_multiple_inheritance_python_many_bases
#define PYBIND11_BASEN(N) \ #define PYBIND11_BASEN(N) \
py::class_<BaseN<(N)>>(m, "BaseN" #N).def(py::init<int>()).def("f" #N, [](BaseN<N> &b) { \ py::class_<BaseN<(N)>>(m, "BaseN" #N).def(py::init<int>()).def("f" #N, [](BaseN<N> &b) { \
return b.i + (N); \ return b.i + (N); \
}) })
PYBIND11_BASEN( 1); PYBIND11_BASEN( 2); PYBIND11_BASEN( 3); PYBIND11_BASEN( 4); PYBIND11_BASEN(1);
PYBIND11_BASEN( 5); PYBIND11_BASEN( 6); PYBIND11_BASEN( 7); PYBIND11_BASEN( 8); PYBIND11_BASEN(2);
PYBIND11_BASEN( 9); PYBIND11_BASEN(10); PYBIND11_BASEN(11); PYBIND11_BASEN(12); PYBIND11_BASEN(3);
PYBIND11_BASEN(13); PYBIND11_BASEN(14); PYBIND11_BASEN(15); PYBIND11_BASEN(16); 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); PYBIND11_BASEN(17);
// Uncommenting this should result in a compile time failure (MI can only be specified via // 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 // template parameters because pybind has to know the types involved; see discussion in #742
// details). // for details).
// struct Base12v2 : Base1, Base2 { // struct Base12v2 : Base1, Base2 {
// Base12v2(int i, int j) : Base1(i), Base2(j) { } // Base12v2(int i, int j) : Base1(i), Base2(j) { }
// }; // };
// py::class_<Base12v2>(m, "Base12v2", b1, b2) // py::class_<Base12v2>(m, "Base12v2", b1, b2)
// .def(py::init<int, int>()); // .def(py::init<int, int>());
// test_multiple_inheritance_virtbase // test_multiple_inheritance_virtbase
// Test the case where not all base classes are specified, and where pybind11 requires the // Test the case where not all base classes are specified, and where pybind11 requires the
@ -139,8 +156,8 @@ TEST_SUBMODULE(multiple_inheritance, m) {
.def(py::init<int>()) .def(py::init<int>())
.def("bar", &Base2a::bar); .def("bar", &Base2a::bar);
py::class_<Base12a, /* Base1 missing */ Base2a, py::class_<Base12a, /* Base1 missing */ Base2a, std::shared_ptr<Base12a>>(
std::shared_ptr<Base12a>>(m, "Base12a", py::multiple_inheritance()) m, "Base12a", py::multiple_inheritance())
.def(py::init<int, int>()); .def(py::init<int, int>());
m.def("bar_base2a", [](Base2a *b) { return b->bar(); }); m.def("bar_base2a", [](Base2a *b) { return b->bar(); });
@ -150,11 +167,18 @@ TEST_SUBMODULE(multiple_inheritance, m) {
// test_mi_base_return // test_mi_base_return
// Issue #801: invalid casting to derived type with MI bases // Issue #801: invalid casting to derived type with MI bases
// Unregistered classes: // Unregistered classes:
struct I801B3 { int c = 3; virtual ~I801B3() = default; }; struct I801B3 {
int c = 3;
virtual ~I801B3() = default;
};
struct I801E : I801B3, I801D {}; struct I801E : I801B3, I801D {};
py::class_<I801B1, std::shared_ptr<I801B1>>(m, "I801B1").def(py::init<>()).def_readonly("a", &I801B1::a); py::class_<I801B1, std::shared_ptr<I801B1>>(m, "I801B1")
py::class_<I801B2, std::shared_ptr<I801B2>>(m, "I801B2").def(py::init<>()).def_readonly("b", &I801B2::b); .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_<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<>()); py::class_<I801D, I801C, std::shared_ptr<I801D>>(m, "I801D").def(py::init<>());
@ -179,11 +203,8 @@ TEST_SUBMODULE(multiple_inheritance, m) {
m.def("i801e_c", []() -> I801C * { return new I801E(); }); m.def("i801e_c", []() -> I801C * { return new I801E(); });
m.def("i801e_b2", []() -> I801B2 * { return new I801E(); }); m.def("i801e_b2", []() -> I801B2 * { return new I801E(); });
// test_mi_static_properties // test_mi_static_properties
py::class_<Vanilla>(m, "Vanilla") py::class_<Vanilla>(m, "Vanilla").def(py::init<>()).def("vanilla", &Vanilla::vanilla);
.def(py::init<>())
.def("vanilla", &Vanilla::vanilla);
py::class_<WithStatic1>(m, "WithStatic1") py::class_<WithStatic1>(m, "WithStatic1")
.def(py::init<>()) .def(py::init<>())
@ -195,22 +216,19 @@ TEST_SUBMODULE(multiple_inheritance, m) {
.def_static("static_func2", &WithStatic2::static_func2) .def_static("static_func2", &WithStatic2::static_func2)
.def_readwrite_static("static_value2", &WithStatic2::static_value2); .def_readwrite_static("static_value2", &WithStatic2::static_value2);
py::class_<VanillaStaticMix1, Vanilla, WithStatic1, WithStatic2>( py::class_<VanillaStaticMix1, Vanilla, WithStatic1, WithStatic2>(m, "VanillaStaticMix1")
m, "VanillaStaticMix1")
.def(py::init<>()) .def(py::init<>())
.def_static("static_func", &VanillaStaticMix1::static_func) .def_static("static_func", &VanillaStaticMix1::static_func)
.def_readwrite_static("static_value", &VanillaStaticMix1::static_value); .def_readwrite_static("static_value", &VanillaStaticMix1::static_value);
py::class_<VanillaStaticMix2, WithStatic1, Vanilla, WithStatic2>( py::class_<VanillaStaticMix2, WithStatic1, Vanilla, WithStatic2>(m, "VanillaStaticMix2")
m, "VanillaStaticMix2")
.def(py::init<>()) .def(py::init<>())
.def_static("static_func", &VanillaStaticMix2::static_func) .def_static("static_func", &VanillaStaticMix2::static_func)
.def_readwrite_static("static_value", &VanillaStaticMix2::static_value); .def_readwrite_static("static_value", &VanillaStaticMix2::static_value);
struct WithDict {};
struct WithDict { }; struct VanillaDictMix1 : Vanilla, WithDict {};
struct VanillaDictMix1 : Vanilla, WithDict { }; struct VanillaDictMix2 : WithDict, Vanilla {};
struct VanillaDictMix2 : WithDict, Vanilla { };
py::class_<WithDict>(m, "WithDict", py::dynamic_attr()).def(py::init<>()); py::class_<WithDict>(m, "WithDict", py::dynamic_attr()).def(py::init<>());
py::class_<VanillaDictMix1, Vanilla, WithDict>(m, "VanillaDictMix1").def(py::init<>()); py::class_<VanillaDictMix1, Vanilla, WithDict>(m, "VanillaDictMix1").def(py::init<>());
py::class_<VanillaDictMix2, WithDict, Vanilla>(m, "VanillaDictMix2").def(py::init<>()); py::class_<VanillaDictMix2, WithDict, Vanilla>(m, "VanillaDictMix2").def(py::init<>());
@ -218,18 +236,25 @@ TEST_SUBMODULE(multiple_inheritance, m) {
// test_diamond_inheritance // test_diamond_inheritance
// Issue #959: segfault when constructing diamond inheritance instance // Issue #959: segfault when constructing diamond inheritance instance
// All of these have int members so that there will be various unequal pointers involved. // 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 B {
struct C0 : public virtual B { int c0; }; int b;
struct C1 : public virtual B { int c1; }; B() = default;
struct D : public C0, public C1 { int d; }; B(const B &) = default;
py::class_<B>(m, "B") virtual ~B() = default;
.def("b", [](B *self) { return self; }); };
py::class_<C0, B>(m, "C0") struct C0 : public virtual B {
.def("c0", [](C0 *self) { return self; }); int c0;
py::class_<C1, B>(m, "C1") };
.def("c1", [](C1 *self) { return self; }); struct C1 : public virtual B {
py::class_<D, C0, C1>(m, "D") int c1;
.def(py::init<>()); };
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_* // test_pr3635_diamond_*
// - functions are get_{base}_{var}, return {var} // - functions are get_{base}_{var}, return {var}

249
tests/test_numpy_array.cpp

@ -7,11 +7,11 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/numpy.h> #include <pybind11/numpy.h>
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "pybind11_tests.h"
#include <cstdint> #include <cstdint>
#include <utility> #include <utility>
@ -22,7 +22,7 @@ struct DtypeCheck {
}; };
template <typename T> template <typename T>
DtypeCheck get_dtype_check(const char* name) { DtypeCheck get_dtype_check(const char *name) {
py::module_ np = py::module_::import("numpy"); py::module_ np = py::module_::import("numpy");
DtypeCheck check{}; DtypeCheck check{};
check.numpy = np.attr("dtype")(np.attr(name)); check.numpy = np.attr("dtype")(np.attr(name));
@ -31,17 +31,15 @@ DtypeCheck get_dtype_check(const char* name) {
} }
std::vector<DtypeCheck> get_concrete_dtype_checks() { std::vector<DtypeCheck> get_concrete_dtype_checks() {
return { return {// Normalization
// Normalization get_dtype_check<std::int8_t>("int8"),
get_dtype_check<std::int8_t>("int8"), get_dtype_check<std::uint8_t>("uint8"),
get_dtype_check<std::uint8_t>("uint8"), get_dtype_check<std::int16_t>("int16"),
get_dtype_check<std::int16_t>("int16"), get_dtype_check<std::uint16_t>("uint16"),
get_dtype_check<std::uint16_t>("uint16"), get_dtype_check<std::int32_t>("int32"),
get_dtype_check<std::int32_t>("int32"), get_dtype_check<std::uint32_t>("uint32"),
get_dtype_check<std::uint32_t>("uint32"), get_dtype_check<std::int64_t>("int64"),
get_dtype_check<std::int64_t>("int64"), get_dtype_check<std::uint64_t>("uint64")};
get_dtype_check<std::uint64_t>("uint64")
};
} }
struct DtypeSizeCheck { struct DtypeSizeCheck {
@ -80,15 +78,18 @@ using arr = py::array;
using arr_t = py::array_t<uint16_t, 0>; using arr_t = py::array_t<uint16_t, 0>;
static_assert(std::is_same<arr_t::value_type, uint16_t>::value, ""); 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...)); 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...)); 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...); auto *ptr = (uint8_t *) a.mutable_data(index...);
for (py::ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) { for (py::ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) {
ptr[i] = (uint8_t) (ptr[i] * 2); ptr[i] = (uint8_t) (ptr[i] * 2);
@ -96,7 +97,8 @@ template<typename... Ix> arr& mutate_data(arr& a, Ix... index) {
return a; 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...); auto ptr = a.mutable_data(index...);
for (py::ssize_t i = 0; i < a.size() - a.index_at(index...); i++) { for (py::ssize_t i = 0; i < a.size() - a.index_at(index...); i++) {
ptr[i]++; ptr[i]++;
@ -104,20 +106,40 @@ template<typename... Ix> arr_t& mutate_data_t(arr_t& a, Ix... index) {
return a; return a;
} }
template<typename... Ix> py::ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } template <typename... Ix>
template<typename... Ix> py::ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } py::ssize_t index_at(const arr &a, Ix... idx) {
template<typename... Ix> py::ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } return a.index_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>
template<typename... Ix> arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; } py::ssize_t index_at_t(const arr_t &a, Ix... idx) {
return a.index_at(idx...);
#define def_index_fn(name, type) \ }
sm.def(#name, [](type a) { return name(a); }); \ template <typename... Ix>
sm.def(#name, [](type a, int i) { return name(a, i); }); \ py::ssize_t offset_at(const arr &a, Ix... idx) {
sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \ 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); }); 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) { if (r.ndim() != 2) {
throw std::domain_error("error: ndim != 2"); throw std::domain_error("error: ndim != 2");
} }
@ -138,16 +160,18 @@ template <typename T, typename T2> py::handle auxiliaries(T &&r, T2 &&r2) {
static int data_i = 42; static int data_i = 42;
TEST_SUBMODULE(numpy_array, sm) { TEST_SUBMODULE(numpy_array, sm) {
try { py::module_::import("numpy"); } try {
catch (...) { return; } py::module_::import("numpy");
} catch (...) {
return;
}
// test_dtypes // test_dtypes
py::class_<DtypeCheck>(sm, "DtypeCheck") py::class_<DtypeCheck>(sm, "DtypeCheck")
.def_readonly("numpy", &DtypeCheck::numpy) .def_readonly("numpy", &DtypeCheck::numpy)
.def_readonly("pybind11", &DtypeCheck::pybind11) .def_readonly("pybind11", &DtypeCheck::pybind11)
.def("__repr__", [](const DtypeCheck& self) { .def("__repr__", [](const DtypeCheck &self) {
return py::str("<DtypeCheck numpy={} pybind11={}>").format( return py::str("<DtypeCheck numpy={} pybind11={}>").format(self.numpy, self.pybind11);
self.numpy, self.pybind11);
}); });
sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks); sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks);
@ -155,41 +179,41 @@ TEST_SUBMODULE(numpy_array, sm) {
.def_readonly("name", &DtypeSizeCheck::name) .def_readonly("name", &DtypeSizeCheck::name)
.def_readonly("size_cpp", &DtypeSizeCheck::size_cpp) .def_readonly("size_cpp", &DtypeSizeCheck::size_cpp)
.def_readonly("size_numpy", &DtypeSizeCheck::size_numpy) .def_readonly("size_numpy", &DtypeSizeCheck::size_numpy)
.def("__repr__", [](const DtypeSizeCheck& self) { .def("__repr__", [](const DtypeSizeCheck &self) {
return py::str("<DtypeSizeCheck name='{}' size_cpp={} size_numpy={} dtype={}>").format( return py::str("<DtypeSizeCheck name='{}' size_cpp={} size_numpy={} dtype={}>")
self.name, self.size_cpp, self.size_numpy, self.dtype); .format(self.name, self.size_cpp, self.size_numpy, self.dtype);
}); });
sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks); sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks);
// test_array_attributes // test_array_attributes
sm.def("ndim", [](const arr& a) { return a.ndim(); }); 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) { return arr(a.ndim(), a.shape()); });
sm.def("shape", [](const arr& a, py::ssize_t dim) { return a.shape(dim); }); 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) { return arr(a.ndim(), a.strides()); });
sm.def("strides", [](const arr& a, py::ssize_t dim) { return a.strides(dim); }); 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("writeable", [](const arr &a) { return a.writeable(); });
sm.def("size", [](const arr& a) { return a.size(); }); sm.def("size", [](const arr &a) { return a.size(); });
sm.def("itemsize", [](const arr& a) { return a.itemsize(); }); sm.def("itemsize", [](const arr &a) { return a.itemsize(); });
sm.def("nbytes", [](const arr& a) { return a.nbytes(); }); sm.def("nbytes", [](const arr &a) { return a.nbytes(); });
sm.def("owndata", [](const arr& a) { return a.owndata(); }); sm.def("owndata", [](const arr &a) { return a.owndata(); });
// test_index_offset // test_index_offset
def_index_fn(index_at, const arr&); def_index_fn(index_at, const arr &);
def_index_fn(index_at_t, const arr_t&); def_index_fn(index_at_t, const arr_t &);
def_index_fn(offset_at, const arr&); def_index_fn(offset_at, const arr &);
def_index_fn(offset_at_t, const arr_t&); def_index_fn(offset_at_t, const arr_t &);
// test_data // test_data
def_index_fn(data, const arr&); def_index_fn(data, const arr &);
def_index_fn(data_t, const arr_t&); def_index_fn(data_t, const arr_t &);
// test_mutate_data, test_mutate_readonly // test_mutate_data, test_mutate_readonly
def_index_fn(mutate_data, arr&); def_index_fn(mutate_data, arr &);
def_index_fn(mutate_data_t, arr_t&); def_index_fn(mutate_data_t, arr_t &);
def_index_fn(at_t, const arr_t&); def_index_fn(at_t, const arr_t &);
def_index_fn(mutate_at_t, arr_t&); def_index_fn(mutate_at_t, arr_t &);
// test_make_c_f_array // test_make_c_f_array
sm.def("make_f_array", [] { return py::array_t<float>({ 2, 2 }, { 4, 8 }); }); 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_c_array", [] { return py::array_t<float>({2, 2}, {8, 4}); });
// test_empty_shaped_array // test_empty_shaped_array
sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); }); sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); });
@ -198,18 +222,16 @@ TEST_SUBMODULE(numpy_array, sm) {
// test_wrap // test_wrap
sm.def("wrap", [](const py::array &a) { sm.def("wrap", [](const py::array &a) {
return py::array( return py::array(a.dtype(),
a.dtype(), {a.shape(), a.shape() + a.ndim()},
{a.shape(), a.shape() + a.ndim()}, {a.strides(), a.strides() + a.ndim()},
{a.strides(), a.strides() + a.ndim()}, a.data(),
a.data(), a);
a
);
}); });
// test_numpy_view // test_numpy_view
struct ArrayClass { struct ArrayClass {
int data[2] = { 1, 2 }; int data[2] = {1, 2};
ArrayClass() { py::print("ArrayClass()"); } ArrayClass() { py::print("ArrayClass()"); }
~ArrayClass() { py::print("~ArrayClass()"); } ~ArrayClass() { py::print("~ArrayClass()"); }
}; };
@ -217,13 +239,12 @@ TEST_SUBMODULE(numpy_array, sm) {
.def(py::init<>()) .def(py::init<>())
.def("numpy_view", [](py::object &obj) { .def("numpy_view", [](py::object &obj) {
py::print("ArrayClass::numpy_view()"); 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); return py::array_t<int>({2}, {4}, a.data, obj);
} });
);
// test_cast_numpy_int64_to_uint64 // test_cast_numpy_int64_to_uint64
sm.def("function_taking_uint64", [](uint64_t) { }); sm.def("function_taking_uint64", [](uint64_t) {});
// test_isinstance // test_isinstance
sm.def("isinstance_untyped", [](py::object yes, py::object no) { sm.def("isinstance_untyped", [](py::object yes, py::object no) {
@ -236,18 +257,14 @@ TEST_SUBMODULE(numpy_array, sm) {
// test_constructors // test_constructors
sm.def("default_constructors", []() { sm.def("default_constructors", []() {
return py::dict( return py::dict("array"_a = py::array(),
"array"_a=py::array(), "array_t<int32>"_a = py::array_t<std::int32_t>(),
"array_t<int32>"_a=py::array_t<std::int32_t>(), "array_t<double>"_a = py::array_t<double>());
"array_t<double>"_a=py::array_t<double>()
);
}); });
sm.def("converting_constructors", [](const py::object &o) { sm.def("converting_constructors", [](const py::object &o) {
return py::dict( return py::dict("array"_a = py::array(o),
"array"_a=py::array(o), "array_t<int32>"_a = py::array_t<std::int32_t>(o),
"array_t<int32>"_a=py::array_t<std::int32_t>(o), "array_t<double>"_a = py::array_t<double>(o));
"array_t<double>"_a=py::array_t<double>(o)
);
}); });
// test_overload_resolution // test_overload_resolution
@ -294,17 +311,21 @@ TEST_SUBMODULE(numpy_array, sm) {
sm.def("issue685", [](const py::object &) { return "other"; }); sm.def("issue685", [](const py::object &) { return "other"; });
// test_array_unchecked_fixed_dims // test_array_unchecked_fixed_dims
sm.def("proxy_add2", [](py::array_t<double> a, double v) { sm.def(
auto r = a.mutable_unchecked<2>(); "proxy_add2",
for (py::ssize_t i = 0; i < r.shape(0); i++) { [](py::array_t<double> a, double v) {
for (py::ssize_t j = 0; j < r.shape(1); j++) { auto r = a.mutable_unchecked<2>();
r(i, j) += v; 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) { 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>(); auto r = a.mutable_unchecked<3>();
for (py::ssize_t i = 0; i < r.shape(0); i++) { for (py::ssize_t i = 0; i < r.shape(0); i++) {
for (py::ssize_t j = 0; j < r.shape(1); j++) { for (py::ssize_t j = 0; j < r.shape(1); j++) {
@ -316,7 +337,7 @@ TEST_SUBMODULE(numpy_array, sm) {
return a; return a;
}); });
sm.def("proxy_init3F", [](double start) { 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>(); auto r = a.mutable_unchecked<3>();
for (py::ssize_t k = 0; k < r.shape(2); k++) { for (py::ssize_t k = 0; k < r.shape(2); k++) {
for (py::ssize_t j = 0; j < r.shape(1); j++) { for (py::ssize_t j = 0; j < r.shape(1); j++) {
@ -356,19 +377,23 @@ TEST_SUBMODULE(numpy_array, sm) {
// test_array_unchecked_dyn_dims // test_array_unchecked_dyn_dims
// Same as the above, but without a compile-time dimensions specification: // Same as the above, but without a compile-time dimensions specification:
sm.def("proxy_add2_dyn", [](py::array_t<double> a, double v) { sm.def(
auto r = a.mutable_unchecked(); "proxy_add2_dyn",
if (r.ndim() != 2) { [](py::array_t<double> a, double v) {
throw std::domain_error("error: ndim != 2"); auto r = a.mutable_unchecked();
} if (r.ndim() != 2) {
for (py::ssize_t i = 0; i < r.shape(0); i++) { throw std::domain_error("error: ndim != 2");
for (py::ssize_t j = 0; j < r.shape(1); j++) {
r(i, j) += v;
} }
} for (py::ssize_t i = 0; i < r.shape(0); i++) {
}, py::arg{}.noconvert(), py::arg()); 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) { 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(); auto r = a.mutable_unchecked();
if (r.ndim() != 3) { if (r.ndim() != 3) {
throw std::domain_error("error: ndim != 3"); throw std::domain_error("error: ndim != 3");
@ -386,29 +411,31 @@ TEST_SUBMODULE(numpy_array, sm) {
return auxiliaries(a.unchecked(), a.mutable_unchecked()); return auxiliaries(a.unchecked(), a.mutable_unchecked());
}); });
sm.def("array_auxiliaries2", [](py::array_t<double> a) { sm.def("array_auxiliaries2", [](py::array_t<double> a) { return auxiliaries(a, a); });
return auxiliaries(a, a);
});
// test_array_failures // 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_fail_test", []() { return py::array(py::object()); });
sm.def("array_t_fail_test", []() { return py::array_t<double>(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: // 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 // test_initializer_list
// Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous
sm.def("array_initializer_list1", []() { return py::array_t<float>(1); }); sm.def("array_initializer_list1", []() { return py::array_t<float>(1); });
// { 1 } also works for the above, but clang warns about it // { 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_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_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_list4", []() { return py::array_t<float>({1, 2, 3, 4}); });
// test_array_resize // test_array_resize
// reshape array to 2D without changing size // reshape array to 2D without changing size
sm.def("array_reshape2", [](py::array_t<double> a) { 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()) { if (dim_sz * dim_sz != a.size()) {
throw std::domain_error( throw std::domain_error(
"array_reshape2: input array total size is not a squared integer"); "array_reshape2: input array total size is not a squared integer");

240
tests/test_numpy_dtypes.cpp

@ -7,13 +7,14 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/numpy.h> #include <pybind11/numpy.h>
#include "pybind11_tests.h"
#ifdef __GNUC__ #ifdef __GNUC__
#define PYBIND11_PACKED(cls) cls __attribute__((__packed__)) # define PYBIND11_PACKED(cls) cls __attribute__((__packed__))
#else #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 #endif
namespace py = pybind11; namespace py = pybind11;
@ -25,7 +26,7 @@ struct SimpleStruct {
long double ldbl_; 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_; return os << "s:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
} }
@ -43,7 +44,7 @@ PYBIND11_PACKED(struct PackedStruct {
long double ldbl_; 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_; return os << "p:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
} }
@ -52,7 +53,7 @@ PYBIND11_PACKED(struct NestedStruct {
PackedStruct b; 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; return os << "n:a=" << v.a << ";b=" << v.b;
} }
@ -70,7 +71,7 @@ struct PartialNestedStruct {
uint64_t dummy2; uint64_t dummy2;
}; };
struct UnboundStruct { }; struct UnboundStruct {};
struct StringStruct { struct StringStruct {
char a[3]; char a[3];
@ -82,7 +83,7 @@ struct ComplexStruct {
std::complex<double> cdbl; 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; return os << "c:" << v.cflt << "," << v.cdbl;
} }
@ -106,7 +107,7 @@ PYBIND11_PACKED(struct EnumStruct {
E2 e2; E2 e2;
}); });
std::ostream& operator<<(std::ostream& os, const StringStruct& v) { std::ostream &operator<<(std::ostream &os, const StringStruct &v) {
os << "a='"; os << "a='";
for (size_t i = 0; i < 3 && (v.a[i] != 0); i++) { for (size_t i = 0; i < 3 && (v.a[i] != 0); i++) {
os << v.a[i]; os << v.a[i];
@ -118,7 +119,7 @@ std::ostream& operator<<(std::ostream& os, const StringStruct& v) {
return os << "'"; return os << "'";
} }
std::ostream& operator<<(std::ostream& os, const ArrayStruct& v) { std::ostream &operator<<(std::ostream &os, const ArrayStruct &v) {
os << "a={"; os << "a={";
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
if (i > 0) { if (i > 0) {
@ -142,15 +143,14 @@ std::ostream& operator<<(std::ostream& os, const ArrayStruct& v) {
return os << '}'; 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"); return os << "e1=" << (v.e1 == E1::A ? "A" : "B") << ",e2=" << (v.e2 == E2::X ? "X" : "Y");
} }
template <typename T> template <typename T>
py::array mkarray_via_buffer(size_t n) { py::array mkarray_via_buffer(size_t n) {
return py::array(py::buffer_info(nullptr, sizeof(T), return py::array(py::buffer_info(
py::format_descriptor<T>::format(), nullptr, sizeof(T), py::format_descriptor<T>::format(), 1, {n}, {sizeof(T)}));
1, { n }, { sizeof(T) }));
} }
#define SET_TEST_VALS(s, i) \ #define SET_TEST_VALS(s, i) \
@ -188,9 +188,9 @@ py::list print_recarray(py::array_t<S, 0> arr) {
py::array_t<int32_t, 0> test_array_ctors(int i) { py::array_t<int32_t, 0> test_array_ctors(int i) {
using arr_t = py::array_t<int32_t, 0>; using arr_t = py::array_t<int32_t, 0>;
std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 }; std::vector<int32_t> data{1, 2, 3, 4, 5, 6};
std::vector<py::ssize_t> shape { 3, 2 }; std::vector<py::ssize_t> shape{3, 2};
std::vector<py::ssize_t> strides { 8, 4 }; std::vector<py::ssize_t> strides{8, 4};
auto *ptr = data.data(); auto *ptr = data.data();
auto *vptr = (void *) ptr; auto *vptr = (void *) ptr;
@ -210,36 +210,62 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
}; };
switch (i) { switch (i) {
// shape: (3, 2) // shape: (3, 2)
case 10: return arr_t(shape, strides, ptr); case 10:
case 11: return py::array(shape, strides, ptr); return arr_t(shape, strides, ptr);
case 12: return py::array(dtype, shape, strides, vptr); case 11:
case 13: return arr_t(shape, ptr); return py::array(shape, strides, ptr);
case 14: return py::array(shape, ptr); case 12:
case 15: return py::array(dtype, shape, vptr); return py::array(dtype, shape, strides, vptr);
case 16: return arr_t(buf_ndim2); case 13:
case 17: return py::array(buf_ndim2); return arr_t(shape, ptr);
// shape: (3, 2) - post-fill case 14:
case 20: return fill(arr_t(shape, strides)); return py::array(shape, ptr);
case 21: return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor case 15:
case 22: return fill(py::array(dtype, shape, strides)); return py::array(dtype, shape, vptr);
case 23: return fill(arr_t(shape)); case 16:
case 24: return py::array(shape, ptr); // can't have nullptr due to templated ctor return arr_t(buf_ndim2);
case 25: return fill(py::array(dtype, shape)); case 17:
case 26: return fill(arr_t(buf_ndim2_null)); return py::array(buf_ndim2);
case 27: return fill(py::array(buf_ndim2_null)); // shape: (3, 2) - post-fill
// shape: (6, ) case 20:
case 30: return arr_t(6, ptr); return fill(arr_t(shape, strides));
case 31: return py::array(6, ptr); case 21:
case 32: return py::array(dtype, 6, vptr); return py::array(shape, strides, ptr); // can't have nullptr due to templated ctor
case 33: return arr_t(buf_ndim1); case 22:
case 34: return py::array(buf_ndim1); return fill(py::array(dtype, shape, strides));
// shape: (6, ) case 23:
case 40: return fill(arr_t(6)); return fill(arr_t(shape));
case 41: return py::array(6, ptr); // can't have nullptr due to templated ctor case 24:
case 42: return fill(py::array(dtype, 6)); return py::array(shape, ptr); // can't have nullptr due to templated ctor
case 43: return fill(arr_t(buf_ndim1_null)); case 25:
case 44: return fill(py::array(buf_ndim1_null)); 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(); return arr_t();
} }
@ -251,9 +277,15 @@ py::list test_dtype_ctors() {
list.append(py::dtype::from_args(py::str("bool"))); list.append(py::dtype::from_args(py::str("bool")));
py::list names, offsets, formats; py::list names, offsets, formats;
py::dict dict; py::dict dict;
names.append(py::str("a")); names.append(py::str("b")); dict["names"] = names; names.append(py::str("a"));
offsets.append(py::int_(1)); offsets.append(py::int_(10)); dict["offsets"] = offsets; names.append(py::str("b"));
formats.append(py::dtype("int32")); formats.append(py::dtype("float64")); dict["formats"] = formats; 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); dict["itemsize"] = py::int_(20);
list.append(py::dtype::from_args(dict)); list.append(py::dtype::from_args(dict));
list.append(py::dtype(names, formats, offsets, 20)); list.append(py::dtype(names, formats, offsets, 20));
@ -266,8 +298,11 @@ struct A {};
struct B {}; struct B {};
TEST_SUBMODULE(numpy_dtypes, m) { TEST_SUBMODULE(numpy_dtypes, m) {
try { py::module_::import("numpy"); } try {
catch (...) { return; } py::module_::import("numpy");
} catch (...) {
return;
}
// typeinfo may be registered before the dtype descriptor for scalar casts to work... // typeinfo may be registered before the dtype descriptor for scalar casts to work...
py::class_<SimpleStruct>(m, "SimpleStruct") py::class_<SimpleStruct>(m, "SimpleStruct")
@ -285,11 +320,10 @@ TEST_SUBMODULE(numpy_dtypes, m) {
if (py::len(tup) != 4) { if (py::len(tup) != 4) {
throw py::cast_error("Invalid size"); throw py::cast_error("Invalid size");
} }
return SimpleStruct{ return SimpleStruct{tup[0].cast<bool>(),
tup[0].cast<bool>(), tup[1].cast<uint32_t>(),
tup[1].cast<uint32_t>(), tup[2].cast<float>(),
tup[2].cast<float>(), tup[3].cast<long double>()};
tup[3].cast<long double>()};
}); });
PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_);
@ -311,18 +345,18 @@ TEST_SUBMODULE(numpy_dtypes, m) {
#ifdef PYBIND11_NEVER_DEFINED_EVER #ifdef PYBIND11_NEVER_DEFINED_EVER
// If enabled, this should produce a static_assert failure telling the user that the struct // If enabled, this should produce a static_assert failure telling the user that the struct
// is not a POD type // 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); PYBIND11_NUMPY_DTYPE(NotPOD, v);
#endif #endif
// Check that dtypes can be registered programmatically, both from // Check that dtypes can be registered programmatically, both from
// initializer lists of field descriptors and from other containers. // 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( 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 // test_recarray, test_scalar_conversion
m.def("create_rec_simple", &create_recarray<SimpleStruct>); m.def("create_rec_simple", &create_recarray<SimpleStruct>);
@ -355,17 +389,15 @@ TEST_SUBMODULE(numpy_dtypes, m) {
m.def("get_format_unbound", []() { return py::format_descriptor<UnboundStruct>::format(); }); m.def("get_format_unbound", []() { return py::format_descriptor<UnboundStruct>::format(); });
m.def("print_format_descriptors", []() { m.def("print_format_descriptors", []() {
py::list l; py::list l;
for (const auto &fmt : { for (const auto &fmt : {py::format_descriptor<SimpleStruct>::format(),
py::format_descriptor<SimpleStruct>::format(), py::format_descriptor<PackedStruct>::format(),
py::format_descriptor<PackedStruct>::format(), py::format_descriptor<NestedStruct>::format(),
py::format_descriptor<NestedStruct>::format(), py::format_descriptor<PartialStruct>::format(),
py::format_descriptor<PartialStruct>::format(), py::format_descriptor<PartialNestedStruct>::format(),
py::format_descriptor<PartialNestedStruct>::format(), py::format_descriptor<StringStruct>::format(),
py::format_descriptor<StringStruct>::format(), py::format_descriptor<ArrayStruct>::format(),
py::format_descriptor<ArrayStruct>::format(), py::format_descriptor<EnumStruct>::format(),
py::format_descriptor<EnumStruct>::format(), py::format_descriptor<ComplexStruct>::format()}) {
py::format_descriptor<ComplexStruct>::format()
}) {
l.append(py::cast(fmt)); l.append(py::cast(fmt));
} }
return l; return l;
@ -373,12 +405,9 @@ TEST_SUBMODULE(numpy_dtypes, m) {
// test_dtype // test_dtype
std::vector<const char *> dtype_names{ std::vector<const char *> dtype_names{
"byte", "short", "intc", "int_", "longlong", "byte", "short", "intc", "int_", "longlong", "ubyte", "ushort",
"ubyte", "ushort", "uintc", "uint", "ulonglong", "uintc", "uint", "ulonglong", "half", "single", "double", "longdouble",
"half", "single", "double", "longdouble", "csingle", "cdouble", "clongdouble", "bool_", "datetime64", "timedelta64", "object_"};
"csingle", "cdouble", "clongdouble",
"bool_", "datetime64", "timedelta64", "object_"
};
m.def("print_dtypes", []() { m.def("print_dtypes", []() {
py::list l; py::list l;
@ -415,9 +444,12 @@ TEST_SUBMODULE(numpy_dtypes, m) {
py::list list; py::list list;
auto dt1 = py::dtype::of<int32_t>(); auto dt1 = py::dtype::of<int32_t>();
auto dt2 = py::dtype::of<SimpleStruct>(); auto dt2 = py::dtype::of<SimpleStruct>();
list.append(dt1); list.append(dt2); list.append(dt1);
list.append(py::bool_(dt1.has_fields())); list.append(py::bool_(dt2.has_fields())); list.append(dt2);
list.append(py::int_(dt1.itemsize())); list.append(py::int_(dt2.itemsize())); 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; return list;
}); });
struct TrailingPaddingStruct { struct TrailingPaddingStruct {
@ -436,14 +468,20 @@ TEST_SUBMODULE(numpy_dtypes, m) {
for (py::ssize_t i = 0; i < req.size * req.itemsize; i++) { for (py::ssize_t i = 0; i < req.size * req.itemsize; i++) {
static_cast<char *>(req.ptr)[i] = 0; static_cast<char *>(req.ptr)[i] = 0;
} }
ptr[1].a[0] = 'a'; ptr[1].b[0] = 'a'; ptr[1].a[0] = 'a';
ptr[2].a[0] = 'a'; ptr[2].b[0] = 'a'; ptr[1].b[0] = 'a';
ptr[3].a[0] = 'a'; ptr[3].b[0] = 'a'; ptr[2].a[0] = 'a';
ptr[2].b[0] = 'a';
ptr[2].a[1] = 'b'; ptr[2].b[1] = 'b'; ptr[3].a[0] = 'a';
ptr[3].a[1] = 'b'; ptr[3].b[1] = 'b'; ptr[3].b[0] = 'a';
ptr[3].a[2] = 'c'; ptr[3].b[2] = 'c'; 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; return arr;
}); });
@ -513,14 +551,19 @@ TEST_SUBMODULE(numpy_dtypes, m) {
PYBIND11_NUMPY_DTYPE(CompareStruct, x, y, z); PYBIND11_NUMPY_DTYPE(CompareStruct, x, y, z);
m.def("compare_buffer_info", []() { m.def("compare_buffer_info", []() {
py::list list; 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<float>::compare(
list.append(py::bool_(py::detail::compare_buffer_info<unsigned>::compare(py::buffer_info(nullptr, sizeof(int), "I", 1)))); py::buffer_info(nullptr, sizeof(float), "f", 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<unsigned>::compare(
list.append(py::bool_(py::detail::compare_buffer_info<long>::compare(py::buffer_info(nullptr, sizeof(long), sizeof(long) == sizeof(int) ? "i" : "q", 1)))); py::buffer_info(nullptr, sizeof(int), "I", 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<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; 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 // test_scalar_conversion
auto f_simple = [](SimpleStruct s) { return s.uint_ * 10; }; auto f_simple = [](SimpleStruct s) { return s.uint_ * 10; };
@ -534,7 +577,8 @@ TEST_SUBMODULE(numpy_dtypes, m) {
m.def("f_simple_pass_thru_vectorized", py::vectorize(f_simple_pass_thru)); m.def("f_simple_pass_thru_vectorized", py::vectorize(f_simple_pass_thru));
// test_register_dtype // 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 // test_str_leak
m.def("dtype_wrapper", [](py::object d) { return py::dtype::from_args(std::move(d)); }); 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 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/numpy.h> #include <pybind11/numpy.h>
#include "pybind11_tests.h"
#include <utility> #include <utility>
double my_func(int x, float y, double z) { 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)); 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) { TEST_SUBMODULE(numpy_vectorize, m) {
try { py::module_::import("numpy"); } try {
catch (...) { return; } py::module_::import("numpy");
} catch (...) {
return;
}
// test_vectorize, test_docs, test_array_collapse // test_vectorize, test_docs, test_array_collapse
// Vectorize all arguments of a function (though non-vector arguments are also allowed) // Vectorize all arguments of a function (though non-vector arguments are also allowed)
m.def("vectorized_func", py::vectorize(my_func)); 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) { 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), return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(std::move(x),
std::move(y)); std::move(y));
}); });
// Vectorize a complex-valued function // Vectorize a complex-valued function
m.def("vectorized_func3", py::vectorize( m.def("vectorized_func3",
[](std::complex<double> c) { return c * std::complex<double>(2.f); } py::vectorize([](std::complex<double> c) { return c * std::complex<double>(2.f); }));
));
// test_type_selection // test_type_selection
// NumPy function which only accepts specific data types // 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", m.def("selective_func",
[](const py::array_t<int, py::array::c_style> &) { return "Int branch taken."; }); [](const py::array_t<int, py::array::c_style> &) { return "Int branch taken."; });
m.def("selective_func", m.def("selective_func",
@ -49,8 +54,8 @@ TEST_SUBMODULE(numpy_vectorize, m) {
}); });
// test_passthrough_arguments // test_passthrough_arguments
// Passthrough test: references and non-pod types should be automatically passed through (in the // Passthrough test: references and non-pod types should be automatically passed through (in
// function definition below, only `b`, `d`, and `g` are vectorized): // the function definition below, only `b`, `d`, and `g` are vectorized):
struct NonPODClass { struct NonPODClass {
explicit NonPODClass(int v) : value{v} {} explicit NonPODClass(int v) : value{v} {}
int value; int value;
@ -76,8 +81,7 @@ TEST_SUBMODULE(numpy_vectorize, m) {
int value = 0; int value = 0;
}; };
py::class_<VectorizeTestClass> vtc(m, "VectorizeTestClass"); py::class_<VectorizeTestClass> vtc(m, "VectorizeTestClass");
vtc .def(py::init<int>()) vtc.def(py::init<int>()).def_readwrite("value", &VectorizeTestClass::value);
.def_readwrite("value", &VectorizeTestClass::value);
// Automatic vectorizing of methods // Automatic vectorizing of methods
vtc.def("method", py::vectorize(&VectorizeTestClass::method)); vtc.def("method", py::vectorize(&VectorizeTestClass::method));
@ -99,5 +103,5 @@ TEST_SUBMODULE(numpy_vectorize, m) {
return py::detail::broadcast(buffers, ndim, shape); 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 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "pybind11_tests.h"
#include <vector> #include <vector>
// IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures // IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures
@ -26,12 +28,13 @@ TEST_SUBMODULE(opaque_types, m) {
.def(py::init<>()) .def(py::init<>())
.def("pop_back", &StringList::pop_back) .def("pop_back", &StringList::pop_back)
/* There are multiple versions of push_back(), etc. Select the right ones. */ /* There are multiple versions of push_back(), etc. Select the right ones. */
.def("push_back", (void (StringList::*)(const std::string &)) &StringList::push_back) .def("push_back", (void(StringList::*)(const std::string &)) & StringList::push_back)
.def("back", (std::string &(StringList::*)()) &StringList::back) .def("back", (std::string & (StringList::*) ()) & StringList::back)
.def("__len__", [](const StringList &v) { return v.size(); }) .def("__len__", [](const StringList &v) { return v.size(); })
.def("__iter__", [](StringList &v) { .def(
return py::make_iterator(v.begin(), v.end()); "__iter__",
}, py::keep_alive<0, 1>()); [](StringList &v) { return py::make_iterator(v.begin(), v.end()); },
py::keep_alive<0, 1>());
class ClassWithSTLVecProperty { class ClassWithSTLVecProperty {
public: public:

176
tests/test_operator_overloading.cpp

@ -7,11 +7,13 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include <pybind11/operators.h>
#include <pybind11/stl.h>
#include "constructor_stats.h" #include "constructor_stats.h"
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <functional> #include <functional>
#include <pybind11/operators.h>
#include <pybind11/stl.h>
class Vector2 { class Vector2 {
public: public:
@ -21,17 +23,24 @@ public:
print_move_created(this); print_move_created(this);
v.x = v.y = 0; 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 { Vector2 &operator=(Vector2 &&v) noexcept {
x = v.x; x = v.x;
y = v.y; y = v.y;
v.x = v.y = 0; v.x = v.y = 0;
print_move_assigned(this); print_move_assigned(this);
return *this; return *this;
} }
~Vector2() { print_destroyed(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 { return Vector2(-x, -y); }
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); }
@ -42,30 +51,51 @@ public:
Vector2 operator/(float value) const { return Vector2(x / value, y / value); } 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) 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) {
Vector2& operator-=(const Vector2 &v) { x -= v.x; y -= v.y; return *this; } x += v.x;
Vector2& operator*=(float v) { x *= v; y *= v; return *this; } y += v.y;
Vector2& operator/=(float v) { x /= v; y /= v; return *this; } 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); }
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 { bool operator==(const Vector2 &v) const { return x == v.x && y == v.y; }
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: private:
float x, y; float x, y;
}; };
class C1 { }; class C1 {};
class C2 { }; class C2 {};
int operator+(const C1 &, const C1 &) { return 11; } int operator+(const C1 &, const C1 &) { return 11; }
int operator+(const C2 &, const C2 &) { return 22; } int operator+(const C2 &, const C2 &) { return 22; }
@ -84,43 +114,41 @@ bool operator==(const HashMe &lhs, const HashMe &rhs) { return lhs.member == rhs
// namespace instead, per this recommendation: // namespace instead, per this recommendation:
// https://en.cppreference.com/w/cpp/language/extending_std#Adding_template_specializations // https://en.cppreference.com/w/cpp/language/extending_std#Adding_template_specializations
namespace std { namespace std {
template<> template <>
struct hash<Vector2> { struct hash<Vector2> {
// Not a good hash function, but easy to test // Not a good hash function, but easy to test
size_t operator()(const Vector2 &) { return 4; } size_t operator()(const Vector2 &) { return 4; }
}; };
// HashMe has a hash function in C++ but no `__hash__` for Python. // HashMe has a hash function in C++ but no `__hash__` for Python.
template <> template <>
struct hash<HashMe> { struct hash<HashMe> {
std::size_t operator()(const HashMe &selector) const { std::size_t operator()(const HashMe &selector) const {
return std::hash<std::string>()(selector.member); return std::hash<std::string>()(selector.member);
} }
}; };
} // namespace std } // namespace std
// Not a good abs function, but easy to test. // Not a good abs function, but easy to test.
std::string abs(const Vector2&) { std::string abs(const Vector2 &) { return "abs(Vector2)"; }
return "abs(Vector2)";
}
// MSVC & Intel warns about unknown pragmas, and warnings are errors. // MSVC & Intel warns about unknown pragmas, and warnings are errors.
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER) #if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic push # pragma GCC diagnostic push
// clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to // 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 `). // `-Wall`, which is used here for overloading (e.g. `py::self += py::self `).
// Here, we suppress the warning using `#pragma diagnostic`. // Here, we suppress the warning using `#pragma diagnostic`.
// Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46 // Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46
// TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`). // TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`).
#if defined(__APPLE__) && defined(__clang__) # if defined(__APPLE__) && defined(__clang__)
#if (__clang_major__ >= 10) # if (__clang_major__ >= 10)
#pragma GCC diagnostic ignored "-Wself-assign-overloaded" # pragma GCC diagnostic ignored "-Wself-assign-overloaded"
#endif # endif
#elif defined(__clang__) # elif defined(__clang__)
#if (__clang_major__ >= 7) # if (__clang_major__ >= 7)
#pragma GCC diagnostic ignored "-Wself-assign-overloaded" # pragma GCC diagnostic ignored "-Wself-assign-overloaded"
#endif # endif
#endif # endif
#endif #endif
TEST_SUBMODULE(operators, m) { TEST_SUBMODULE(operators, m) {
@ -154,46 +182,52 @@ TEST_SUBMODULE(operators, m) {
.def(py::hash(py::self)) .def(py::hash(py::self))
// N.B. See warning about usage of `py::detail::abs(py::self)` in // N.B. See warning about usage of `py::detail::abs(py::self)` in
// `operators.h`. // `operators.h`.
.def("__abs__", [](const Vector2& v) { return abs(v); }) .def("__abs__", [](const Vector2 &v) { return abs(v); });
;
m.attr("Vector") = m.attr("Vector2"); m.attr("Vector") = m.attr("Vector2");
// test_operators_notimplemented // test_operators_notimplemented
// #393: need to return NotSupported to ensure correct arithmetic operator behavior // #393: need to return NotSupported to ensure correct arithmetic operator behavior
py::class_<C1>(m, "C1") py::class_<C1>(m, "C1").def(py::init<>()).def(py::self + py::self);
.def(py::init<>())
.def(py::self + py::self);
py::class_<C2>(m, "C2") py::class_<C2>(m, "C2")
.def(py::init<>()) .def(py::init<>())
.def(py::self + py::self) .def(py::self + py::self)
.def("__add__", [](const C2& c2, const C1& c1) { return c2 + c1; }) .def("__add__", [](const C2 &c2, const C1 &c1) { return c2 + c1; })
.def("__radd__", [](const C2& c2, const C1& c1) { return c1 + c2; }); .def("__radd__", [](const C2 &c2, const C1 &c1) { return c1 + c2; });
// test_nested // test_nested
// #328: first member in a class can't be used in operators // #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") py::class_<NestABase>(m, "NestABase")
.def(py::init<>()) .def(py::init<>())
.def_readwrite("value", &NestABase::value); .def_readwrite("value", &NestABase::value);
struct NestA : NestABase { struct NestA : NestABase {
int value = 3; int value = 3;
NestA& operator+=(int i) { value += i; return *this; } NestA &operator+=(int i) {
value += i;
return *this;
}
}; };
py::class_<NestA>(m, "NestA") py::class_<NestA>(m, "NestA")
.def(py::init<>()) .def(py::init<>())
.def(py::self += int()) .def(py::self += int())
.def("as_base", [](NestA &a) -> NestABase& { .def(
return (NestABase&) a; "as_base",
}, py::return_value_policy::reference_internal); [](NestA &a) -> NestABase & { return (NestABase &) a; },
py::return_value_policy::reference_internal);
m.def("get_NestA", [](const NestA &a) { return a.value; }); m.def("get_NestA", [](const NestA &a) { return a.value; });
struct NestB { struct NestB {
NestA a; NestA a;
int value = 4; int value = 4;
NestB& operator-=(int i) { value -= i; return *this; } NestB &operator-=(int i) {
value -= i;
return *this;
}
}; };
py::class_<NestB>(m, "NestB") py::class_<NestB>(m, "NestB")
.def(py::init<>()) .def(py::init<>())
@ -204,7 +238,10 @@ TEST_SUBMODULE(operators, m) {
struct NestC { struct NestC {
NestB b; NestB b;
int value = 5; int value = 5;
NestC& operator*=(int i) { value *= i; return *this; } NestC &operator*=(int i) {
value *= i;
return *this;
}
}; };
py::class_<NestC>(m, "NestC") py::class_<NestC>(m, "NestC")
.def(py::init<>()) .def(py::init<>())
@ -212,16 +249,15 @@ TEST_SUBMODULE(operators, m) {
.def_readwrite("b", &NestC::b); .def_readwrite("b", &NestC::b);
m.def("get_NestC", [](const NestC &c) { return c.value; }); m.def("get_NestC", [](const NestC &c) { return c.value; });
// test_overriding_eq_reset_hash // test_overriding_eq_reset_hash
// #2191 Overriding __eq__ should set __hash__ to None // #2191 Overriding __eq__ should set __hash__ to None
struct Comparable { struct Comparable {
int value; 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 { 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); } size_t hash() const { return static_cast<size_t>(value); }
}; };
@ -229,9 +265,7 @@ TEST_SUBMODULE(operators, m) {
using Hashable::Hashable; using Hashable::Hashable;
}; };
py::class_<Comparable>(m, "Comparable") py::class_<Comparable>(m, "Comparable").def(py::init<int>()).def(py::self == py::self);
.def(py::init<int>())
.def(py::self == py::self);
py::class_<Hashable>(m, "Hashable") py::class_<Hashable>(m, "Hashable")
.def(py::init<int>()) .def(py::init<int>())
@ -250,5 +284,5 @@ TEST_SUBMODULE(operators, m) {
m.def("get_unhashable_HashMe_set", []() { return std::unordered_set<HashMe>{{"one"}}; }); m.def("get_unhashable_HashMe_set", []() { return std::unordered_set<HashMe>{{"one"}}; });
} }
#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER) #if !defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif

6
tests/test_pickling.cpp

@ -20,11 +20,11 @@
namespace exercise_trampoline { namespace exercise_trampoline {
struct SimpleBase { struct SimpleBase {
int num = 0; int num = 0;
virtual ~SimpleBase() = default; virtual ~SimpleBase() = default;
// For compatibility with old clang versions: // For compatibility with old clang versions:
SimpleBase() = default; SimpleBase() = default;
SimpleBase(const SimpleBase &) = default; SimpleBase(const SimpleBase &) = default;
}; };
@ -50,7 +50,7 @@ void wrap(py::module m) {
} }
auto cpp_state = std::unique_ptr<SimpleBase>(new SimpleBaseTrampoline); auto cpp_state = std::unique_ptr<SimpleBase>(new SimpleBaseTrampoline);
cpp_state->num = t[0].cast<int>(); 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); return std::make_pair(std::move(cpp_state), py_state);
})); }));

167
tests/test_pytypes.cpp

@ -7,22 +7,21 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include <utility>
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include <utility>
TEST_SUBMODULE(pytypes, m) { TEST_SUBMODULE(pytypes, m) {
// test_bool // test_bool
m.def("get_bool", []{return py::bool_(false);}); m.def("get_bool", [] { return py::bool_(false); });
// test_int // test_int
m.def("get_int", []{return py::int_(0);}); m.def("get_int", [] { return py::int_(0); });
// test_iterator // test_iterator
m.def("get_iterator", []{return py::iterator();}); m.def("get_iterator", [] { return py::iterator(); });
// test_iterable // test_iterable
m.def("get_iterable", []{return py::iterable();}); m.def("get_iterable", [] { return py::iterable(); });
// test_float // test_float
m.def("get_float", []{return py::float_(0.0f);}); m.def("get_float", [] { return py::float_(0.0f); });
// test_list // test_list
m.def("list_no_args", []() { return py::list{}; }); m.def("list_no_args", []() { return py::list{}; });
m.def("list_ssize_t", []() { return py::list{(py::ssize_t) 0}; }); m.def("list_ssize_t", []() { return py::list{(py::ssize_t) 0}; });
@ -45,7 +44,7 @@ TEST_SUBMODULE(pytypes, m) {
} }
}); });
// test_none // 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)); }); m.def("print_none", [](const py::none &none) { py::print("none: {}"_s.format(none)); });
// test_set // test_set
@ -66,15 +65,15 @@ TEST_SUBMODULE(pytypes, m) {
m.def("set_contains", [](const py::set &set, const char *key) { return set.contains(key); }); m.def("set_contains", [](const py::set &set, const char *key) { return set.contains(key); });
// test_dict // 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) { m.def("print_dict", [](const py::dict &dict) {
for (auto item : dict) { for (auto item : dict) {
py::print("key: {}, value={}"_s.format(item.first, item.second)); py::print("key: {}, value={}"_s.format(item.first, item.second));
} }
}); });
m.def("dict_keyword_constructor", []() { m.def("dict_keyword_constructor", []() {
auto d1 = py::dict("x"_a=1, "y"_a=2); auto d1 = py::dict("x"_a = 1, "y"_a = 2);
auto d2 = py::dict("z"_a=3, **d1); auto d2 = py::dict("z"_a = 3, **d1);
return d2; return d2;
}); });
m.def("dict_contains", m.def("dict_contains",
@ -91,7 +90,8 @@ TEST_SUBMODULE(pytypes, m) {
#if PY_VERSION_HEX >= 0x03030000 #if PY_VERSION_HEX >= 0x03030000
// test_simple_namespace // test_simple_namespace
m.def("get_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::delattr(ns, "wrong");
py::setattr(ns, "right", py::int_(2)); py::setattr(ns, "right", py::int_(2));
return ns; return ns;
@ -103,16 +103,15 @@ TEST_SUBMODULE(pytypes, m) {
m.def("str_from_char_size_t", []() { return py::str{"blue", (py::size_t) 4}; }); m.def("str_from_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_string", []() { return py::str(std::string("baz")); });
m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); }); 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("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("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_handle", [](py::handle h) { return py::str(h); });
m.def("str_from_string_from_str", [](const py::str& obj) { m.def("str_from_string_from_str",
return py::str(static_cast<std::string>(obj)); [](const py::str &obj) { return py::str(static_cast<std::string>(obj)); });
});
m.def("str_format", []() { m.def("str_format", []() {
auto s1 = "{} + {} = {}"_s.format(1, 2, 3); 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); return py::make_tuple(s1, s2);
}); });
@ -131,9 +130,7 @@ TEST_SUBMODULE(pytypes, m) {
// test_capsule // test_capsule
m.def("return_capsule_with_destructor", []() { m.def("return_capsule_with_destructor", []() {
py::print("creating capsule"); py::print("creating capsule");
return py::capsule([]() { return py::capsule([]() { py::print("destructing capsule"); });
py::print("destructing capsule");
});
}); });
m.def("return_capsule_with_destructor_2", []() { m.def("return_capsule_with_destructor_2", []() {
@ -148,23 +145,23 @@ TEST_SUBMODULE(pytypes, m) {
if (ptr) { if (ptr) {
const auto *name = PyCapsule_GetName(ptr); const auto *name = PyCapsule_GetName(ptr);
py::print("destructing capsule ({}, '{}')"_s.format( 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); capsule.set_pointer((void *) 1234);
// Using get_pointer<T>() // Using get_pointer<T>()
void* contents1 = static_cast<void*>(capsule); void *contents1 = static_cast<void *>(capsule);
void* contents2 = capsule.get_pointer(); void *contents2 = capsule.get_pointer();
void* contents3 = capsule.get_pointer<void>(); void *contents3 = capsule.get_pointer<void>();
auto result1 = reinterpret_cast<size_t>(contents1); auto result1 = reinterpret_cast<size_t>(contents1);
auto result2 = reinterpret_cast<size_t>(contents2); auto result2 = reinterpret_cast<size_t>(contents2);
auto result3 = reinterpret_cast<size_t>(contents3); 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; return capsule;
}); });
@ -244,51 +241,45 @@ TEST_SUBMODULE(pytypes, m) {
// test_constructors // test_constructors
m.def("default_constructors", []() { m.def("default_constructors", []() {
return py::dict( return py::dict("bytes"_a = py::bytes(),
"bytes"_a=py::bytes(), "bytearray"_a = py::bytearray(),
"bytearray"_a=py::bytearray(), "str"_a = py::str(),
"str"_a=py::str(), "bool"_a = py::bool_(),
"bool"_a=py::bool_(), "int"_a = py::int_(),
"int"_a=py::int_(), "float"_a = py::float_(),
"float"_a=py::float_(), "tuple"_a = py::tuple(),
"tuple"_a=py::tuple(), "list"_a = py::list(),
"list"_a=py::list(), "dict"_a = py::dict(),
"dict"_a=py::dict(), "set"_a = py::set());
"set"_a=py::set()
);
}); });
m.def("converting_constructors", [](const py::dict &d) { m.def("converting_constructors", [](const py::dict &d) {
return py::dict( return py::dict("bytes"_a = py::bytes(d["bytes"]),
"bytes"_a=py::bytes(d["bytes"]), "bytearray"_a = py::bytearray(d["bytearray"]),
"bytearray"_a=py::bytearray(d["bytearray"]), "str"_a = py::str(d["str"]),
"str"_a=py::str(d["str"]), "bool"_a = py::bool_(d["bool"]),
"bool"_a=py::bool_(d["bool"]), "int"_a = py::int_(d["int"]),
"int"_a=py::int_(d["int"]), "float"_a = py::float_(d["float"]),
"float"_a=py::float_(d["float"]), "tuple"_a = py::tuple(d["tuple"]),
"tuple"_a=py::tuple(d["tuple"]), "list"_a = py::list(d["list"]),
"list"_a=py::list(d["list"]), "dict"_a = py::dict(d["dict"]),
"dict"_a=py::dict(d["dict"]), "set"_a = py::set(d["set"]),
"set"_a=py::set(d["set"]), "memoryview"_a = py::memoryview(d["memoryview"]));
"memoryview"_a=py::memoryview(d["memoryview"])
);
}); });
m.def("cast_functions", [](const py::dict &d) { m.def("cast_functions", [](const py::dict &d) {
// When converting between Python types, obj.cast<T>() should be the same as T(obj) // When converting between Python types, obj.cast<T>() should be the same as T(obj)
return py::dict( return py::dict("bytes"_a = d["bytes"].cast<py::bytes>(),
"bytes"_a=d["bytes"].cast<py::bytes>(), "bytearray"_a = d["bytearray"].cast<py::bytearray>(),
"bytearray"_a=d["bytearray"].cast<py::bytearray>(), "str"_a = d["str"].cast<py::str>(),
"str"_a=d["str"].cast<py::str>(), "bool"_a = d["bool"].cast<py::bool_>(),
"bool"_a=d["bool"].cast<py::bool_>(), "int"_a = d["int"].cast<py::int_>(),
"int"_a=d["int"].cast<py::int_>(), "float"_a = d["float"].cast<py::float_>(),
"float"_a=d["float"].cast<py::float_>(), "tuple"_a = d["tuple"].cast<py::tuple>(),
"tuple"_a=d["tuple"].cast<py::tuple>(), "list"_a = d["list"].cast<py::list>(),
"list"_a=d["list"].cast<py::list>(), "dict"_a = d["dict"].cast<py::dict>(),
"dict"_a=d["dict"].cast<py::dict>(), "set"_a = d["set"].cast<py::set>(),
"set"_a=d["set"].cast<py::set>(), "memoryview"_a = d["memoryview"].cast<py::memoryview>());
"memoryview"_a=d["memoryview"].cast<py::memoryview>()
);
}); });
m.def("convert_to_pybind11_str", [](const py::object &o) { return py::str(o); }); m.def("convert_to_pybind11_str", [](const py::object &o) { return py::str(o); });
@ -341,10 +332,7 @@ TEST_SUBMODULE(pytypes, m) {
l.append(py::cast(12)); l.append(py::cast(12));
l.append(py::int_(15)); l.append(py::int_(15));
return py::dict( return py::dict("d"_a = d, "l"_a = l);
"d"_a=d,
"l"_a=l
);
}); });
// test_print // test_print
@ -352,16 +340,17 @@ TEST_SUBMODULE(pytypes, m) {
py::print("Hello, World!"); py::print("Hello, World!");
py::print(1, 2.0, "three", true, std::string("-- multiple args")); py::print(1, 2.0, "three", true, std::string("-- multiple args"));
auto args = py::make_tuple("and", "a", "custom", "separator"); auto args = py::make_tuple("and", "a", "custom", "separator");
py::print("*args", *args, "sep"_a="-"); py::print("*args", *args, "sep"_a = "-");
py::print("no new line here", "end"_a=" -- "); py::print("no new line here", "end"_a = " -- ");
py::print("next print"); py::print("next print");
auto py_stderr = py::module_::import("sys").attr("stderr"); 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()); }); m.def("print_failure", []() { py::print(42, UnregisteredType()); });
@ -406,8 +395,8 @@ TEST_SUBMODULE(pytypes, m) {
[](const py::buffer &b) { return py::memoryview(b.request()); }); [](const py::buffer &b) { return py::memoryview(b.request()); });
m.def("test_memoryview_from_buffer", [](bool is_unsigned) { m.def("test_memoryview_from_buffer", [](bool is_unsigned) {
static const int16_t si16[] = { 3, 1, 4, 1, 5 }; static const int16_t si16[] = {3, 1, 4, 1, 5};
static const uint16_t ui16[] = { 2, 7, 1, 8 }; static const uint16_t ui16[] = {2, 7, 1, 8};
if (is_unsigned) { if (is_unsigned) {
return py::memoryview::from_buffer(ui16, {4}, {sizeof(uint16_t)}); return py::memoryview::from_buffer(ui16, {4}, {sizeof(uint16_t)});
} }
@ -415,32 +404,29 @@ TEST_SUBMODULE(pytypes, m) {
}); });
m.def("test_memoryview_from_buffer_nativeformat", []() { m.def("test_memoryview_from_buffer_nativeformat", []() {
static const char* format = "@i"; static const char *format = "@i";
static const int32_t arr[] = { 4, 7, 5 }; static const int32_t arr[] = {4, 7, 5};
return py::memoryview::from_buffer( return py::memoryview::from_buffer(arr, sizeof(int32_t), format, {3}, {sizeof(int32_t)});
arr, sizeof(int32_t), format, { 3 }, { sizeof(int32_t) });
}); });
m.def("test_memoryview_from_buffer_empty_shape", []() { m.def("test_memoryview_from_buffer_empty_shape", []() {
static const char* buf = ""; static const char *buf = "";
return py::memoryview::from_buffer(buf, 1, "B", { }, { }); return py::memoryview::from_buffer(buf, 1, "B", {}, {});
}); });
m.def("test_memoryview_from_buffer_invalid_strides", []() { m.def("test_memoryview_from_buffer_invalid_strides", []() {
static const char* buf = "\x02\x03\x04"; static const char *buf = "\x02\x03\x04";
return py::memoryview::from_buffer(buf, 1, "B", { 3 }, { }); return py::memoryview::from_buffer(buf, 1, "B", {3}, {});
}); });
m.def("test_memoryview_from_buffer_nullptr", []() { m.def("test_memoryview_from_buffer_nullptr", []() {
return py::memoryview::from_buffer( return py::memoryview::from_buffer(static_cast<void *>(nullptr), 1, "B", {}, {});
static_cast<void*>(nullptr), 1, "B", { }, { });
}); });
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
m.def("test_memoryview_from_memory", []() { m.def("test_memoryview_from_memory", []() {
const char* buf = "\xff\xe1\xab\x37"; const char *buf = "\xff\xe1\xab\x37";
return py::memoryview::from_memory( return py::memoryview::from_memory(buf, static_cast<py::ssize_t>(strlen(buf)));
buf, static_cast<py::ssize_t>(strlen(buf)));
}); });
#endif #endif
@ -461,8 +447,7 @@ TEST_SUBMODULE(pytypes, m) {
m.def("pass_to_std_string", [](const std::string &s) { return s.size(); }); m.def("pass_to_std_string", [](const std::string &s) { return s.size(); });
// test_weakref // test_weakref
m.def("weakref_from_handle", m.def("weakref_from_handle", [](py::handle h) { return py::weakref(h); });
[](py::handle h) { return py::weakref(h); });
m.def("weakref_from_handle_and_function", m.def("weakref_from_handle_and_function",
[](py::handle h, py::function f) { return py::weakref(h, std::move(f)); }); [](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); }); m.def("weakref_from_object", [](const py::object &o) { return py::weakref(o); });

225
tests/test_sequences_and_iterators.cpp

@ -8,44 +8,52 @@
BSD-style license that can be found in the LICENSE file. 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/operators.h>
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <vector> #include <vector>
#ifdef PYBIND11_HAS_OPTIONAL #ifdef PYBIND11_HAS_OPTIONAL
#include <optional> # include <optional>
#endif // PYBIND11_HAS_OPTIONAL #endif // PYBIND11_HAS_OPTIONAL
template<typename T> template <typename T>
class NonZeroIterator { class NonZeroIterator {
const T* ptr_; const T *ptr_;
public: public:
explicit NonZeroIterator(const T *ptr) : ptr_(ptr) {} explicit NonZeroIterator(const T *ptr) : ptr_(ptr) {}
const T& operator*() const { return *ptr_; } const T &operator*() const { return *ptr_; }
NonZeroIterator& operator++() { ++ptr_; return *this; } NonZeroIterator &operator++() {
++ptr_;
return *this;
}
}; };
class NonZeroSentinel {}; class NonZeroSentinel {};
template<typename A, typename B> template <typename A, typename B>
bool operator==(const NonZeroIterator<std::pair<A, B>>& it, const NonZeroSentinel&) { bool operator==(const NonZeroIterator<std::pair<A, B>> &it, const NonZeroSentinel &) {
return !(*it).first || !(*it).second; return !(*it).first || !(*it).second;
} }
/* Iterator where dereferencing returns prvalues instead of references. */ /* Iterator where dereferencing returns prvalues instead of references. */
template<typename T> template <typename T>
class NonRefIterator { class NonRefIterator {
const T* ptr_; const T *ptr_;
public: public:
explicit NonRefIterator(const T *ptr) : ptr_(ptr) {} explicit NonRefIterator(const T *ptr) : ptr_(ptr) {}
T operator*() const { return T(*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_; } bool operator==(const NonRefIterator &other) const { return ptr_ == other.ptr_; }
}; };
@ -54,17 +62,18 @@ public:
explicit NonCopyableInt(int value) : value_(value) {} explicit NonCopyableInt(int value) : value_(value) {}
NonCopyableInt(const NonCopyableInt &) = delete; NonCopyableInt(const NonCopyableInt &) = delete;
NonCopyableInt(NonCopyableInt &&other) noexcept : value_(other.value_) { 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=(const NonCopyableInt &) = delete;
NonCopyableInt &operator=(NonCopyableInt &&other) noexcept { NonCopyableInt &operator=(NonCopyableInt &&other) noexcept {
value_ = other.value_; value_ = other.value_;
other.value_ = -1; // detect when an unwanted move occurs other.value_ = -1; // detect when an unwanted move occurs
return *this; return *this;
} }
int get() const { return value_; } int get() const { return value_; }
void set(int value) { value_ = value; } void set(int value) { value_ = value; }
~NonCopyableInt() = default; ~NonCopyableInt() = default;
private: private:
int value_; int value_;
}; };
@ -81,7 +90,9 @@ py::list test_random_access_iterator(PythonType x) {
auto checks = py::list(); auto checks = py::list();
auto assert_equal = [&checks](py::handle a, py::handle b) { auto assert_equal = [&checks](py::handle a, py::handle b) {
auto result = PyObject_RichCompareBool(a.ptr(), b.ptr(), Py_EQ); 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); checks.append(result != 0);
}; };
@ -116,7 +127,7 @@ py::list test_random_access_iterator(PythonType x) {
TEST_SUBMODULE(sequences_and_iterators, m) { TEST_SUBMODULE(sequences_and_iterators, m) {
// test_sliceable // test_sliceable
class Sliceable{ class Sliceable {
public: public:
explicit Sliceable(int n) : size(n) {} explicit Sliceable(int n) : size(n) {}
int start, stop, step; int start, stop, step;
@ -130,18 +141,20 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
throw py::error_already_set(); throw py::error_already_set();
} }
int istart = static_cast<int>(start); int istart = static_cast<int>(start);
int istop = static_cast<int>(stop); int istop = static_cast<int>(stop);
int istep = static_cast<int>(step); int istep = static_cast<int>(step);
return std::make_tuple(istart, istop, istep); return std::make_tuple(istart, istop, istep);
}); });
m.def("make_forward_slice_size_t", []() { return py::slice(0, -1, 1); }); 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 #ifdef PYBIND11_HAS_OPTIONAL
m.attr("has_optional") = true; m.attr("has_optional") = true;
m.def("make_reversed_slice_size_t_optional_verbose", []() { return py::slice(std::nullopt, std::nullopt, -1); }); m.def("make_reversed_slice_size_t_optional_verbose",
// Warning: The following spelling may still compile if optional<> is not present and give wrong answers. []() { return py::slice(std::nullopt, std::nullopt, -1); });
// Please use with caution. // 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); }); m.def("make_reversed_slice_size_t_optional", []() { return py::slice({}, {}, -1); });
#else #else
m.attr("has_optional") = false; m.attr("has_optional") = false;
@ -166,7 +179,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
print_copy_created(this); print_copy_created(this);
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
m_data = new float[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);
} }
Sequence(Sequence &&s) noexcept : m_size(s.m_size), m_data(s.m_data) { Sequence(Sequence &&s) noexcept : m_size(s.m_size), m_data(s.m_data) {
print_move_created(this); print_move_created(this);
@ -174,14 +187,17 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
s.m_data = nullptr; s.m_data = nullptr;
} }
~Sequence() { print_destroyed(this); delete[] m_data; } ~Sequence() {
print_destroyed(this);
delete[] m_data;
}
Sequence &operator=(const Sequence &s) { Sequence &operator=(const Sequence &s) {
if (&s != this) { if (&s != this) {
delete[] m_data; delete[] m_data;
m_size = s.m_size; m_size = s.m_size;
m_data = new float[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); print_copy_assigned(this);
return *this; return *this;
@ -235,7 +251,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
size_t size() const { return m_size; } size_t size() const { return m_size; }
const float *begin() const { return m_data; } 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: private:
size_t m_size; size_t m_size;
@ -303,8 +319,8 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
; ;
// test_map_iterator // test_map_iterator
// Interface of a map-like object that isn't (directly) an unordered_map, but provides some basic // Interface of a map-like object that isn't (directly) an unordered_map, but provides some
// map-like functionality. // basic map-like functionality.
class StringMap { class StringMap {
public: public:
StringMap() = default; StringMap() = default;
@ -314,8 +330,10 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
void set(const std::string &key, std::string val) { map[key] = std::move(val); } 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); } std::string get(const std::string &key) const { return map.at(key); }
size_t size() const { return map.size(); } size_t size() const { return map.size(); }
private: private:
std::unordered_map<std::string, std::string> map; std::unordered_map<std::string, std::string> map;
public: public:
decltype(map.cbegin()) begin() const { return map.cbegin(); } decltype(map.cbegin()) begin() const { return map.cbegin(); }
decltype(map.cend()) end() const { return map.cend(); } decltype(map.cend()) end() const { return map.cend(); }
@ -350,90 +368,115 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
class IntPairs { class IntPairs {
public: public:
explicit IntPairs(std::vector<std::pair<int, int>> data) : data_(std::move(data)) {} 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 // .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: private:
std::vector<std::pair<int, int>> data_; std::vector<std::pair<int, int>> data_;
}; };
py::class_<IntPairs>(m, "IntPairs") py::class_<IntPairs>(m, "IntPairs")
.def(py::init<std::vector<std::pair<int, int>>>()) .def(py::init<std::vector<std::pair<int, int>>>())
.def("nonzero", [](const IntPairs& s) { .def(
return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel()); "nonzero",
}, py::keep_alive<0, 1>()) [](const IntPairs &s) {
.def("nonzero_keys", [](const IntPairs& s) { return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
return py::make_key_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel()); NonZeroSentinel());
}, py::keep_alive<0, 1>()) },
.def("nonzero_values", [](const IntPairs& s) { py::keep_alive<0, 1>())
return py::make_value_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel()); .def(
}, py::keep_alive<0, 1>()) "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 // test iterator that returns values instead of references
.def("nonref", [](const IntPairs& s) { .def(
return py::make_iterator(NonRefIterator<std::pair<int, int>>(s.begin()), "nonref",
NonRefIterator<std::pair<int, int>>(s.end())); [](const IntPairs &s) {
}, py::keep_alive<0, 1>()) return py::make_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
.def("nonref_keys", [](const IntPairs& s) { NonRefIterator<std::pair<int, int>>(s.end()));
return py::make_key_iterator(NonRefIterator<std::pair<int, int>>(s.begin()), },
NonRefIterator<std::pair<int, int>>(s.end())); py::keep_alive<0, 1>())
}, py::keep_alive<0, 1>()) .def(
.def("nonref_values", [](const IntPairs& s) { "nonref_keys",
return py::make_value_iterator(NonRefIterator<std::pair<int, int>>(s.begin()), [](const IntPairs &s) {
NonRefIterator<std::pair<int, int>>(s.end())); return py::make_key_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
}, py::keep_alive<0, 1>()) 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 // test single-argument make_iterator
.def("simple_iterator", [](IntPairs& self) { .def(
return py::make_iterator(self); "simple_iterator",
}, py::keep_alive<0, 1>()) [](IntPairs &self) { return py::make_iterator(self); },
.def("simple_keys", [](IntPairs& self) { py::keep_alive<0, 1>())
return py::make_key_iterator(self); .def(
}, py::keep_alive<0, 1>()) "simple_keys",
.def("simple_values", [](IntPairs& self) { [](IntPairs &self) { return py::make_key_iterator(self); },
return py::make_value_iterator(self); py::keep_alive<0, 1>())
}, 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 // 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 // at runtime, but tests need to be able to compile with the correct
// overload. See PR #3293. // overload. See PR #3293.
.def("_make_iterator_extras", [](IntPairs& self) { .def(
return py::make_iterator(self, py::call_guard<int>()); "_make_iterator_extras",
}, py::keep_alive<0, 1>()) [](IntPairs &self) { return py::make_iterator(self, py::call_guard<int>()); },
.def("_make_key_extras", [](IntPairs& self) { py::keep_alive<0, 1>())
return py::make_key_iterator(self, py::call_guard<int>()); .def(
}, py::keep_alive<0, 1>()) "_make_key_extras",
.def("_make_value_extras", [](IntPairs& self) { [](IntPairs &self) { return py::make_key_iterator(self, py::call_guard<int>()); },
return py::make_value_iterator(self, py::call_guard<int>()); py::keep_alive<0, 1>())
}, 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 // test_iterater_referencing
py::class_<NonCopyableInt>(m, "NonCopyableInt") py::class_<NonCopyableInt>(m, "NonCopyableInt")
.def(py::init<int>()) .def(py::init<int>())
.def("set", &NonCopyableInt::set) .def("set", &NonCopyableInt::set)
.def("__int__", &NonCopyableInt::get) .def("__int__", &NonCopyableInt::get);
;
py::class_<std::vector<NonCopyableInt>>(m, "VectorNonCopyableInt") py::class_<std::vector<NonCopyableInt>>(m, "VectorNonCopyableInt")
.def(py::init<>()) .def(py::init<>())
.def("append", [](std::vector<NonCopyableInt> &vec, int value) { .def("append",
vec.emplace_back(value); [](std::vector<NonCopyableInt> &vec, int value) { vec.emplace_back(value); })
})
.def("__iter__", [](std::vector<NonCopyableInt> &vec) { .def("__iter__", [](std::vector<NonCopyableInt> &vec) {
return py::make_iterator(vec.begin(), vec.end()); return py::make_iterator(vec.begin(), vec.end());
}) });
;
py::class_<std::vector<NonCopyableIntPair>>(m, "VectorNonCopyableIntPair") py::class_<std::vector<NonCopyableIntPair>>(m, "VectorNonCopyableIntPair")
.def(py::init<>()) .def(py::init<>())
.def("append", [](std::vector<NonCopyableIntPair> &vec, const std::pair<int, int> &value) { .def("append",
vec.emplace_back(NonCopyableInt(value.first), NonCopyableInt(value.second)); [](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("keys",
}) [](std::vector<NonCopyableIntPair> &vec) {
return py::make_key_iterator(vec.begin(), vec.end());
})
.def("values", [](std::vector<NonCopyableIntPair> &vec) { .def("values", [](std::vector<NonCopyableIntPair> &vec) {
return py::make_value_iterator(vec.begin(), vec.end()); return py::make_value_iterator(vec.begin(), vec.end());
}) });
;
#if 0 #if 0
// Obsolete: special data structure for exposing custom iterator types to python // Obsolete: special data structure for exposing custom iterator types to python
@ -511,7 +554,9 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
// test_iterator_rvp // test_iterator_rvp
// #388: Can't make iterators via make_iterator() with different r/v policies // #388: Can't make iterators via make_iterator() with different r/v policies
static std::vector<int> list = { 1, 2, 3 }; 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_1",
m.def("make_iterator_2", []() { return py::make_iterator<py::return_value_policy::automatic>(list); }); []() { 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 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC #if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC
# pragma warning(disable: 4702) // unreachable code in system header (xatomic.h(382)) # pragma warning(disable : 4702) // unreachable code in system header (xatomic.h(382))
#endif #endif
#include "pybind11_tests.h"
#include "object.h" #include "object.h"
#include "pybind11_tests.h"
namespace { namespace {
// This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the // 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 // holder size to trigger the non-simple-layout internal instance layout for single inheritance
// large holder type: // with large holder type:
template <typename T> class huge_unique_ptr { template <typename T>
class huge_unique_ptr {
std::unique_ptr<T> ptr; std::unique_ptr<T> ptr;
uint64_t padding[10]; uint64_t padding[10];
public: public:
explicit huge_unique_ptr(T *p) : ptr(p) {} explicit huge_unique_ptr(T *p) : ptr(p) {}
T *get() { return ptr.get(); } T *get() { return ptr.get(); }
@ -32,10 +34,11 @@ public:
template <typename T> template <typename T>
class custom_unique_ptr { class custom_unique_ptr {
std::unique_ptr<T> impl; std::unique_ptr<T> impl;
public: public:
explicit custom_unique_ptr(T *p) : impl(p) {} explicit custom_unique_ptr(T *p) : impl(p) {}
T* get() const { return impl.get(); } T *get() const { return impl.get(); }
T* release_ptr() { return impl.release(); } T *release_ptr() { return impl.release(); }
}; };
// Simple custom holder that works like shared_ptr and has operator& overload // Simple custom holder that works like shared_ptr and has operator& overload
@ -44,11 +47,12 @@ public:
template <typename T> template <typename T>
class shared_ptr_with_addressof_operator { class shared_ptr_with_addressof_operator {
std::shared_ptr<T> impl; std::shared_ptr<T> impl;
public: public:
shared_ptr_with_addressof_operator( ) = default; shared_ptr_with_addressof_operator() = default;
explicit shared_ptr_with_addressof_operator(T *p) : impl(p) {} explicit shared_ptr_with_addressof_operator(T *p) : impl(p) {}
T* get() const { return impl.get(); } T *get() const { return impl.get(); }
T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); } 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 // Simple custom holder that works like unique_ptr and has operator& overload
@ -57,12 +61,13 @@ public:
template <typename T> template <typename T>
class unique_ptr_with_addressof_operator { class unique_ptr_with_addressof_operator {
std::unique_ptr<T> impl; std::unique_ptr<T> impl;
public: public:
unique_ptr_with_addressof_operator() = default; unique_ptr_with_addressof_operator() = default;
explicit unique_ptr_with_addressof_operator(T *p) : impl(p) {} explicit unique_ptr_with_addressof_operator(T *p) : impl(p) {}
T* get() const { return impl.get(); } T *get() const { return impl.get(); }
T* release_ptr() { return impl.release(); } T *release_ptr() { return impl.release(); }
T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); } 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) // Custom object with builtin reference counting (see 'object.h' for the implementation)
@ -70,8 +75,10 @@ class MyObject1 : public Object {
public: public:
explicit MyObject1(int value) : value(value) { print_created(this, toString()); } explicit MyObject1(int value) : value(value) { print_created(this, toString()); }
std::string toString() const override { return "MyObject1[" + std::to_string(value) + "]"; } std::string toString() const override { return "MyObject1[" + std::to_string(value) + "]"; }
protected: protected:
~MyObject1() override { print_destroyed(this); } ~MyObject1() override { print_destroyed(this); }
private: private:
int value; int value;
}; };
@ -83,6 +90,7 @@ public:
explicit MyObject2(int value) : value(value) { print_created(this, toString()); } explicit MyObject2(int value) : value(value) { print_created(this, toString()); }
std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; } std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; }
virtual ~MyObject2() { print_destroyed(this); } virtual ~MyObject2() { print_destroyed(this); }
private: private:
int value; int value;
}; };
@ -94,6 +102,7 @@ public:
explicit MyObject3(int value) : value(value) { print_created(this, toString()); } explicit MyObject3(int value) : value(value) { print_created(this, toString()); }
std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; } std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; }
virtual ~MyObject3() { print_destroyed(this); } virtual ~MyObject3() { print_destroyed(this); }
private: private:
int value; int value;
}; };
@ -117,6 +126,7 @@ public:
delete o; delete o;
} }
} }
private: private:
~MyObject4() { ~MyObject4() {
myobject4_instances.erase(this); myobject4_instances.erase(this);
@ -144,6 +154,7 @@ public:
delete o; delete o;
} }
} }
protected: protected:
virtual ~MyObject4a() { virtual ~MyObject4a() {
myobject4a_instances.erase(this); myobject4a_instances.erase(this);
@ -232,14 +243,14 @@ struct TypeForMoveOnlyHolderWithAddressOf {
}; };
// test_smart_ptr_from_default // test_smart_ptr_from_default
struct HeldByDefaultHolder { }; struct HeldByDefaultHolder {};
// test_shared_ptr_gc // test_shared_ptr_gc
// #187: issue involving std::shared_ptr<> return value policy & garbage collection // #187: issue involving std::shared_ptr<> return value policy & garbage collection
struct ElementBase { struct ElementBase {
virtual ~ElementBase() = default; /* Force creation of virtual table */ virtual ~ElementBase() = default; /* Force creation of virtual table */
ElementBase() = default; ElementBase() = default;
ElementBase(const ElementBase&) = delete; ElementBase(const ElementBase &) = delete;
}; };
struct ElementA : ElementBase { struct ElementA : ElementBase {
@ -259,11 +270,12 @@ struct ElementList {
// It is always possible to construct a ref<T> from an Object* pointer without // It is always possible to construct a ref<T> from an Object* pointer without
// possible inconsistencies, hence the 'true' argument at the end. // possible inconsistencies, hence the 'true' argument at the end.
// Make pybind11 aware of the non-standard getter member function // Make pybind11 aware of the non-standard getter member function
namespace pybind11 { namespace detail { namespace pybind11 {
template <typename T> namespace detail {
struct holder_helper<ref<T>> { template <typename T>
static const T *get(const ref<T> &p) { return p.get_ptr(); } struct holder_helper<ref<T>> {
}; static const T *get(const ref<T> &p) { return p.get_ptr(); }
};
} // namespace detail } // namespace detail
} // namespace pybind11 } // namespace pybind11
@ -287,8 +299,7 @@ TEST_SUBMODULE(smart_ptr, m) {
py::class_<Object, ref<Object>> obj(m, "Object"); py::class_<Object, ref<Object>> obj(m, "Object");
obj.def("getRefCount", &Object::getRefCount); obj.def("getRefCount", &Object::getRefCount);
py::class_<MyObject1, ref<MyObject1>>(m, "MyObject1", obj) py::class_<MyObject1, ref<MyObject1>>(m, "MyObject1", obj).def(py::init<int>());
.def(py::init<int>());
py::implicitly_convertible<py::int_, MyObject1>(); py::implicitly_convertible<py::int_, MyObject1>();
m.def("make_object_1", []() -> Object * { return new MyObject1(1); }); m.def("make_object_1", []() -> Object * { return new MyObject1(1); });
@ -307,25 +318,27 @@ TEST_SUBMODULE(smart_ptr, m) {
// Expose constructor stats for the ref type // Expose constructor stats for the ref type
m.def("cstats_ref", &ConstructorStats::get<ref_tag>); m.def("cstats_ref", &ConstructorStats::get<ref_tag>);
py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2") py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2").def(py::init<int>());
.def(py::init<int>());
m.def("make_myobject2_1", []() { return new MyObject2(6); }); m.def("make_myobject2_1", []() { return new MyObject2(6); });
m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); }); m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); });
m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); }); m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); });
// NOLINTNEXTLINE(performance-unnecessary-value-param) // NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("print_myobject2_2", [](std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); }); 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_3",
m.def("print_myobject2_4", [](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); }); [](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") py::class_<MyObject3, std::shared_ptr<MyObject3>>(m, "MyObject3").def(py::init<int>());
.def(py::init<int>());
m.def("make_myobject3_1", []() { return new MyObject3(8); }); m.def("make_myobject3_1", []() { return new MyObject3(8); });
m.def("make_myobject3_2", []() { return std::make_shared<MyObject3>(9); }); m.def("make_myobject3_2", []() { return std::make_shared<MyObject3>(9); });
m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); }); m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); });
// NOLINTNEXTLINE(performance-unnecessary-value-param) // NOLINTNEXTLINE(performance-unnecessary-value-param)
m.def("print_myobject3_2", [](std::shared_ptr<MyObject3> obj) { py::print(obj->toString()); }); 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_3",
m.def("print_myobject3_4", [](const std::shared_ptr<MyObject3> *obj) { py::print((*obj)->toString()); }); [](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 // test_smart_ptr_refcounting
m.def("test_object1_refcounting", []() { m.def("test_object1_refcounting", []() {
@ -421,11 +434,18 @@ TEST_SUBMODULE(smart_ptr, m) {
[](const HolderWithAddressOf *obj) { py::print((*obj).get()->toString()); }); [](const HolderWithAddressOf *obj) { py::print((*obj).get()->toString()); });
// test_move_only_holder_with_addressof_operator // test_move_only_holder_with_addressof_operator
using MoveOnlyHolderWithAddressOf = unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>; using MoveOnlyHolderWithAddressOf
py::class_<TypeForMoveOnlyHolderWithAddressOf, MoveOnlyHolderWithAddressOf>(m, "TypeForMoveOnlyHolderWithAddressOf") = unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>;
.def_static("make", []() { return MoveOnlyHolderWithAddressOf(new TypeForMoveOnlyHolderWithAddressOf(0)); }) py::class_<TypeForMoveOnlyHolderWithAddressOf, MoveOnlyHolderWithAddressOf>(
m, "TypeForMoveOnlyHolderWithAddressOf")
.def_static("make",
[]() {
return MoveOnlyHolderWithAddressOf(
new TypeForMoveOnlyHolderWithAddressOf(0));
})
.def_readwrite("value", &TypeForMoveOnlyHolderWithAddressOf::value) .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 // test_smart_ptr_from_default
py::class_<HeldByDefaultHolder, std::unique_ptr<HeldByDefaultHolder>>(m, "HeldByDefaultHolder") py::class_<HeldByDefaultHolder, std::unique_ptr<HeldByDefaultHolder>>(m, "HeldByDefaultHolder")

236
tests/test_stl.cpp

@ -7,39 +7,43 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
#ifndef PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL #ifndef PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL
#define PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL # define PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL
#endif #endif
#include <pybind11/stl/filesystem.h> #include <pybind11/stl/filesystem.h>
#include <vector>
#include <string> #include <string>
#include <vector>
#if defined(PYBIND11_TEST_BOOST) #if defined(PYBIND11_TEST_BOOST)
#include <boost/optional.hpp> # include <boost/optional.hpp>
namespace pybind11 { namespace detail { namespace pybind11 {
namespace detail {
template <typename T> template <typename T>
struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {}; struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {};
template <> template <>
struct type_caster<boost::none_t> : void_caster<boost::none_t> {}; struct type_caster<boost::none_t> : void_caster<boost::none_t> {};
}} // namespace pybind11::detail } // namespace detail
} // namespace pybind11
#endif #endif
// Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 // Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14
#if defined(PYBIND11_HAS_VARIANT) #if defined(PYBIND11_HAS_VARIANT)
using std::variant; using std::variant;
#elif defined(PYBIND11_TEST_BOOST) && (!defined(_MSC_VER) || _MSC_VER >= 1910) #elif defined(PYBIND11_TEST_BOOST) && (!defined(_MSC_VER) || _MSC_VER >= 1910)
# include <boost/variant.hpp> # include <boost/variant.hpp>
# define PYBIND11_HAS_VARIANT 1 # define PYBIND11_HAS_VARIANT 1
using boost::variant; using boost::variant;
namespace pybind11 { namespace detail { namespace pybind11 {
namespace detail {
template <typename... Ts> template <typename... Ts>
struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {}; struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};
@ -50,7 +54,8 @@ struct visit_helper<boost::variant> {
return boost::apply_visitor(args...); return boost::apply_visitor(args...);
} }
}; };
}} // namespace pybind11::detail } // namespace detail
} // namespace pybind11
#endif #endif
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>); PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
@ -63,26 +68,23 @@ struct TplCtorClass {
}; };
namespace std { namespace std {
template <> template <>
struct hash<TplCtorClass> { size_t operator()(const TplCtorClass &) const { return 0; } }; struct hash<TplCtorClass> {
size_t operator()(const TplCtorClass &) const { return 0; }
};
} // namespace std } // namespace std
template <template <typename> class OptionalImpl, typename T> template <template <typename> class OptionalImpl, typename T>
struct OptionalHolder struct OptionalHolder {
{
// NOLINTNEXTLINE(modernize-use-equals-default): breaks GCC 4.8 // NOLINTNEXTLINE(modernize-use-equals-default): breaks GCC 4.8
OptionalHolder() {}; OptionalHolder(){};
bool member_initialized() const { bool member_initialized() const { return member && member->initialized; }
return member && member->initialized;
}
OptionalImpl<T> member = T{}; OptionalImpl<T> member = T{};
}; };
enum class EnumType { enum class EnumType {
kSet = 42, kSet = 42,
kUnset = 85, kUnset = 85,
}; };
// This is used to test that return-by-ref and return-by-copy policies are // This is used to test that return-by-ref and return-by-copy policies are
@ -102,7 +104,7 @@ public:
value = EnumType::kUnset; value = EnumType::kUnset;
} }
OptionalEnumValue& access_by_ref() { return value; } OptionalEnumValue &access_by_ref() { return value; }
OptionalEnumValue access_by_copy() { return value; } OptionalEnumValue access_by_copy() { return value; }
private: private:
@ -122,62 +124,56 @@ public:
ReferenceSensitiveOptional() = default; ReferenceSensitiveOptional() = default;
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
ReferenceSensitiveOptional(const T& value) : storage{value} {} ReferenceSensitiveOptional(const T &value) : storage{value} {}
// NOLINTNEXTLINE(google-explicit-constructor) // NOLINTNEXTLINE(google-explicit-constructor)
ReferenceSensitiveOptional(T&& value) : storage{std::move(value)} {} ReferenceSensitiveOptional(T &&value) : storage{std::move(value)} {}
ReferenceSensitiveOptional& operator=(const T& value) { ReferenceSensitiveOptional &operator=(const T &value) {
storage = {value}; storage = {value};
return *this; return *this;
} }
ReferenceSensitiveOptional& operator=(T&& value) { ReferenceSensitiveOptional &operator=(T &&value) {
storage = {std::move(value)}; storage = {std::move(value)};
return *this; return *this;
} }
template <typename... Args> template <typename... Args>
T& emplace(Args&&... args) { T &emplace(Args &&...args) {
storage.clear(); storage.clear();
storage.emplace_back(std::forward<Args>(args)...); storage.emplace_back(std::forward<Args>(args)...);
return storage.back(); return storage.back();
} }
const T& value() const noexcept { const T &value() const noexcept {
assert(!storage.empty()); assert(!storage.empty());
return storage[0]; return storage[0];
} }
const T& operator*() const noexcept { const T &operator*() const noexcept { return value(); }
return value();
}
const T* operator->() const noexcept { const T *operator->() const noexcept { return &value(); }
return &value();
}
explicit operator bool() const noexcept { explicit operator bool() const noexcept { return !storage.empty(); }
return !storage.empty();
}
private: private:
std::vector<T> storage; std::vector<T> storage;
}; };
namespace pybind11 { namespace detail { namespace pybind11 {
namespace detail {
template <typename T> template <typename T>
struct type_caster<ReferenceSensitiveOptional<T>> : optional_caster<ReferenceSensitiveOptional<T>> {}; struct type_caster<ReferenceSensitiveOptional<T>>
: optional_caster<ReferenceSensitiveOptional<T>> {};
} // namespace detail } // namespace detail
} // namespace pybind11 } // namespace pybind11
TEST_SUBMODULE(stl, m) { TEST_SUBMODULE(stl, m) {
// test_vector // test_vector
m.def("cast_vector", []() { return std::vector<int>{1}; }); 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; }); 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 // `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("cast_bool_vector", []() { return std::vector<bool>{true, false}; });
m.def("load_bool_vector", [](const std::vector<bool> &v) { m.def("load_bool_vector",
return v.at(0) == true && v.at(1) == false; [](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 // Unnumbered regression (caused by #936): pointers to stl containers aren't castable
static std::vector<RValueCaster> lvv{2}; static std::vector<RValueCaster> lvv{2};
m.def("cast_ptr_vector", []() { return &lvv; }); m.def("cast_ptr_vector", []() { return &lvv; });
@ -187,12 +183,12 @@ TEST_SUBMODULE(stl, m) {
m.def("load_deque", [](const std::deque<int> &v) { return v.at(0) == 1 && v.at(1) == 2; }); m.def("load_deque", [](const std::deque<int> &v) { return v.at(0) == 1 && v.at(1) == 2; });
// test_array // 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; }); m.def("load_array", [](const std::array<int, 2> &a) { return a[0] == 1 && a[1] == 2; });
// test_valarray // test_valarray
m.def("cast_valarray", []() { return std::valarray<int>{1, 4, 9}; }); 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; return v.size() == 3 && v[0] == 1 && v[1] == 4 && v[2] == 9;
}); });
@ -214,10 +210,12 @@ TEST_SUBMODULE(stl, m) {
// NB: map and set keys are `const`, so while we technically do move them (as `const Type &&`), // 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 &` // casters don't typically do anything with that, which means they fall to the `const Type &`
// caster. // 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", []() { m.def("cast_rv_nested", []() {
std::vector<std::array<std::list<std::unordered_map<std::string, RValueCaster>>, 2>> v; 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].emplace_back(); // add a map to the array
v.back()[0].back().emplace("b", RValueCaster{}); v.back()[0].back().emplace("b", RValueCaster{});
v.back()[0].back().emplace("c", RValueCaster{}); v.back()[0].back().emplace("c", RValueCaster{});
@ -226,13 +224,15 @@ TEST_SUBMODULE(stl, m) {
return v; return v;
}); });
static std::array<RValueCaster, 2> lva; static std::array<RValueCaster, 2> lva;
static std::unordered_map<std::string, RValueCaster> lvm{{"a", RValueCaster{}}, {"b", RValueCaster{}}}; static std::unordered_map<std::string, RValueCaster> lvm{{"a", RValueCaster{}},
static std::unordered_map<std::string, std::vector<std::list<std::array<RValueCaster, 2>>>> lvn; {"b", RValueCaster{}}};
lvn["a"].emplace_back(); // add a list 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"].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["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 an array
lvn["b"].back().emplace_back(); // add another array lvn["b"].back().emplace_back(); // add another array
m.def("cast_lv_vector", []() -> const decltype(lvv) & { return lvv; }); m.def("cast_lv_vector", []() -> const decltype(lvv) & { return lvv; });
@ -253,7 +253,9 @@ TEST_SUBMODULE(stl, m) {
// test_move_out_container // test_move_out_container
struct MoveOutContainer { struct MoveOutContainer {
struct Value { int value; }; struct Value {
int value;
};
std::list<Value> move_list() const { return {{0}, {1}, {2}}; } std::list<Value> move_list() const { return {{0}, {1}, {2}}; }
}; };
py::class_<MoveOutContainer::Value>(m, "MoveOutContainerValue") py::class_<MoveOutContainer::Value>(m, "MoveOutContainerValue")
@ -266,7 +268,7 @@ TEST_SUBMODULE(stl, m) {
struct NoAssign { struct NoAssign {
int value; int value;
explicit NoAssign(int value = 0) : value(value) { } explicit NoAssign(int value = 0) : value(value) {}
NoAssign(const NoAssign &) = default; NoAssign(const NoAssign &) = default;
NoAssign(NoAssign &&) = default; NoAssign(NoAssign &&) = default;
@ -277,13 +279,10 @@ TEST_SUBMODULE(stl, m) {
.def(py::init<>()) .def(py::init<>())
.def(py::init<int>()); .def(py::init<int>());
struct MoveOutDetector {
struct MoveOutDetector
{
MoveOutDetector() = default; MoveOutDetector() = default;
MoveOutDetector(const MoveOutDetector&) = default; MoveOutDetector(const MoveOutDetector &) = default;
MoveOutDetector(MoveOutDetector&& other) noexcept MoveOutDetector(MoveOutDetector &&other) noexcept : initialized(other.initialized) {
: initialized(other.initialized) {
// steal underlying resource // steal underlying resource
other.initialized = false; other.initialized = false;
} }
@ -293,23 +292,22 @@ TEST_SUBMODULE(stl, m) {
.def(py::init<>()) .def(py::init<>())
.def_readonly("initialized", &MoveOutDetector::initialized); .def_readonly("initialized", &MoveOutDetector::initialized);
#ifdef PYBIND11_HAS_OPTIONAL #ifdef PYBIND11_HAS_OPTIONAL
// test_optional // test_optional
m.attr("has_optional") = true; m.attr("has_optional") = true;
using opt_int = std::optional<int>; using opt_int = std::optional<int>;
using opt_no_assign = std::optional<NoAssign>; using opt_no_assign = std::optional<NoAssign>;
m.def("double_or_zero", [](const opt_int& x) -> int { m.def("double_or_zero", [](const opt_int &x) -> int { return x.value_or(0) * 2; });
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("half_or_none", [](int x) -> opt_int { return x != 0 ? opt_int(x / 2) : opt_int(); });
m.def("test_nullopt", [](opt_int x) { m.def(
return x.value_or(42); "test_nullopt",
}, py::arg_v("x", std::nullopt, "None")); [](opt_int x) { return x.value_or(42); },
m.def("test_no_assign", [](const opt_no_assign &x) { py::arg_v("x", std::nullopt, "None"));
return x ? x->value : 42; m.def(
}, py::arg_v("x", std::nullopt, "None")); "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", [](std::optional<int>) { return true; });
m.def("nodefer_none_optional", [](const py::none &) { return false; }); m.def("nodefer_none_optional", [](const py::none &) { return false; });
@ -333,18 +331,17 @@ TEST_SUBMODULE(stl, m) {
using exp_opt_int = std::experimental::optional<int>; using exp_opt_int = std::experimental::optional<int>;
using exp_opt_no_assign = std::experimental::optional<NoAssign>; using exp_opt_no_assign = std::experimental::optional<NoAssign>;
m.def("double_or_zero_exp", [](const exp_opt_int& x) -> int { m.def("double_or_zero_exp", [](const exp_opt_int &x) -> int { return x.value_or(0) * 2; });
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("half_or_none_exp", [](int x) -> exp_opt_int { m.def(
return x ? exp_opt_int(x / 2) : exp_opt_int(); "test_nullopt_exp",
}); [](exp_opt_int x) { return x.value_or(42); },
m.def("test_nullopt_exp", [](exp_opt_int x) { py::arg_v("x", std::experimental::nullopt, "None"));
return x.value_or(42); m.def(
}, py::arg_v("x", std::experimental::nullopt, "None")); "test_no_assign_exp",
m.def("test_no_assign_exp", [](const exp_opt_no_assign &x) { [](const exp_opt_no_assign &x) { return x ? x->value : 42; },
return x ? x->value : 42; py::arg_v("x", std::experimental::nullopt, "None"));
}, py::arg_v("x", std::experimental::nullopt, "None"));
using opt_exp_holder = OptionalHolder<std::experimental::optional, MoveOutDetector>; using opt_exp_holder = OptionalHolder<std::experimental::optional, MoveOutDetector>;
py::class_<opt_exp_holder>(m, "OptionalExpHolder", "Class with optional member") py::class_<opt_exp_holder>(m, "OptionalExpHolder", "Class with optional member")
@ -365,18 +362,17 @@ TEST_SUBMODULE(stl, m) {
using boost_opt_int = boost::optional<int>; using boost_opt_int = boost::optional<int>;
using boost_opt_no_assign = boost::optional<NoAssign>; using boost_opt_no_assign = boost::optional<NoAssign>;
m.def("double_or_zero_boost", [](const boost_opt_int& x) -> int { m.def("double_or_zero_boost", [](const boost_opt_int &x) -> int { return x.value_or(0) * 2; });
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("half_or_none_boost", [](int x) -> boost_opt_int { m.def(
return x != 0 ? boost_opt_int(x / 2) : boost_opt_int(); "test_nullopt_boost",
}); [](boost_opt_int x) { return x.value_or(42); },
m.def("test_nullopt_boost", [](boost_opt_int x) { py::arg_v("x", boost::none, "None"));
return x.value_or(42); m.def(
}, py::arg_v("x", boost::none, "None")); "test_no_assign_boost",
m.def("test_no_assign_boost", [](const boost_opt_no_assign &x) { [](const boost_opt_no_assign &x) { return x ? x->value : 42; },
return x ? x->value : 42; py::arg_v("x", boost::none, "None"));
}, py::arg_v("x", boost::none, "None"));
using opt_boost_holder = OptionalHolder<boost::optional, MoveOutDetector>; using opt_boost_holder = OptionalHolder<boost::optional, MoveOutDetector>;
py::class_<opt_boost_holder>(m, "OptionalBoostHolder", "Class with optional member") py::class_<opt_boost_holder>(m, "OptionalBoostHolder", "Class with optional member")
@ -394,9 +390,8 @@ TEST_SUBMODULE(stl, m) {
// test_refsensitive_optional // test_refsensitive_optional
using refsensitive_opt_int = ReferenceSensitiveOptional<int>; using refsensitive_opt_int = ReferenceSensitiveOptional<int>;
using refsensitive_opt_no_assign = ReferenceSensitiveOptional<NoAssign>; using refsensitive_opt_no_assign = ReferenceSensitiveOptional<NoAssign>;
m.def("double_or_zero_refsensitive", [](const refsensitive_opt_int& x) -> int { m.def("double_or_zero_refsensitive",
return (x ? x.value() : 0) * 2; [](const refsensitive_opt_int &x) -> int { return (x ? x.value() : 0) * 2; });
});
m.def("half_or_none_refsensitive", [](int x) -> refsensitive_opt_int { m.def("half_or_none_refsensitive", [](int x) -> refsensitive_opt_int {
return x != 0 ? refsensitive_opt_int(x / 2) : refsensitive_opt_int(); return x != 0 ? refsensitive_opt_int(x / 2) : refsensitive_opt_int();
}); });
@ -405,12 +400,14 @@ TEST_SUBMODULE(stl, m) {
// NOLINTNEXTLINE(performance-unnecessary-value-param) // NOLINTNEXTLINE(performance-unnecessary-value-param)
[](refsensitive_opt_int x) { return x ? x.value() : 42; }, [](refsensitive_opt_int x) { return x ? x.value() : 42; },
py::arg_v("x", refsensitive_opt_int(), "None")); py::arg_v("x", refsensitive_opt_int(), "None"));
m.def("test_no_assign_refsensitive", [](const refsensitive_opt_no_assign &x) { m.def(
return x ? x->value : 42; "test_no_assign_refsensitive",
}, py::arg_v("x", refsensitive_opt_no_assign(), "None")); [](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>; 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(py::init<>())
.def_readonly("member", &opt_refsensitive_holder::member) .def_readonly("member", &opt_refsensitive_holder::member)
.def("member_initialized", &opt_refsensitive_holder::member_initialized); .def("member_initialized", &opt_refsensitive_holder::member_initialized);
@ -424,7 +421,7 @@ TEST_SUBMODULE(stl, m) {
#ifdef PYBIND11_HAS_FILESYSTEM #ifdef PYBIND11_HAS_FILESYSTEM
// test_fs_path // test_fs_path
m.attr("has_filesystem") = true; 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 #endif
#ifdef PYBIND11_HAS_VARIANT #ifdef PYBIND11_HAS_VARIANT
@ -472,13 +469,13 @@ TEST_SUBMODULE(stl, m) {
// #171: Can't return STL structures containing reference wrapper // #171: Can't return STL structures containing reference wrapper
m.def("return_vec_of_reference_wrapper", [](std::reference_wrapper<UserType> p4) { m.def("return_vec_of_reference_wrapper", [](std::reference_wrapper<UserType> p4) {
static UserType p1{1}, p2{2}, p3{3}; static UserType p1{1}, p2{2}, p3{3};
return std::vector<std::reference_wrapper<UserType>> { return std::vector<std::reference_wrapper<UserType>>{
std::ref(p1), std::ref(p2), std::ref(p3), p4 std::ref(p1), std::ref(p2), std::ref(p3), p4};
};
}); });
// test_stl_pass_by_pointer // 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> // #1258: pybind11/stl.h converts string to vector<string>
m.def("func_with_string_or_vector_string_arg_overload", m.def("func_with_string_or_vector_string_arg_overload",
@ -496,19 +493,24 @@ TEST_SUBMODULE(stl, m) {
py::class_<Placeholder>(m, "Placeholder"); py::class_<Placeholder>(m, "Placeholder");
/// test_stl_vector_ownership /// test_stl_vector_ownership
m.def("test_stl_ownership", m.def(
[]() { "test_stl_ownership",
std::vector<Placeholder *> result; []() {
result.push_back(new Placeholder()); std::vector<Placeholder *> result;
return result; result.push_back(new Placeholder());
}, return result;
py::return_value_policy::take_ownership); },
py::return_value_policy::take_ownership);
m.def("array_cast_sequence", [](std::array<int, 3> x) { return x; }); m.def("array_cast_sequence", [](std::array<int, 3> x) { return x; });
/// test_issue_1561 /// test_issue_1561
struct Issue1561Inner { std::string data; }; struct Issue1561Inner {
struct Issue1561Outer { std::vector<Issue1561Inner> list; }; std::string data;
};
struct Issue1561Outer {
std::vector<Issue1561Inner> list;
};
py::class_<Issue1561Inner>(m, "Issue1561Inner") py::class_<Issue1561Inner>(m, "Issue1561Inner")
.def(py::init<std::string>()) .def(py::init<std::string>())

54
tests/test_stl_binders.cpp

@ -7,12 +7,13 @@
BSD-style license that can be found in the LICENSE file. 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_tests.h"
#include <pybind11/stl_bind.h>
#include <pybind11/numpy.h>
#include <map>
#include <deque> #include <deque>
#include <map>
#include <unordered_map> #include <unordered_map>
class El { class El {
@ -23,7 +24,7 @@ public:
int a; int a;
}; };
std::ostream & operator<<(std::ostream &s, El const&v) { std::ostream &operator<<(std::ostream &s, El const &v) {
s << "El{" << v.a << '}'; s << "El{" << v.a << '}';
return s; return s;
} }
@ -40,7 +41,8 @@ public:
int value; int value;
}; };
template <class Container> Container *one_to_n(int n) { template <class Container>
Container *one_to_n(int n) {
auto *v = new Container(); auto *v = new Container();
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
v->emplace_back(i); v->emplace_back(i);
@ -48,7 +50,8 @@ template <class Container> Container *one_to_n(int n) {
return v; return v;
} }
template <class Map> Map *times_ten(int n) { template <class Map>
Map *times_ten(int n) {
auto *m = new Map(); auto *m = new Map();
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
m->emplace(int(i), E_nc(10 * i)); m->emplace(int(i), E_nc(10 * i));
@ -56,7 +59,8 @@ template <class Map> Map *times_ten(int n) {
return m; return m;
} }
template <class NestMap> NestMap *times_hundred(int n) { template <class NestMap>
NestMap *times_hundred(int n) {
auto *m = new NestMap(); auto *m = new NestMap();
for (int i = 1; i <= n; i++) { for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) { for (int j = 1; j <= n; j++) {
@ -71,8 +75,7 @@ TEST_SUBMODULE(stl_binders, m) {
py::bind_vector<std::vector<unsigned int>>(m, "VectorInt", py::buffer_protocol()); py::bind_vector<std::vector<unsigned int>>(m, "VectorInt", py::buffer_protocol());
// test_vector_custom // test_vector_custom
py::class_<El>(m, "El") py::class_<El>(m, "El").def(py::init<int>());
.def(py::init<int>());
py::bind_vector<std::vector<El>>(m, "VectorEl"); py::bind_vector<std::vector<El>>(m, "VectorEl");
py::bind_vector<std::vector<std::vector<El>>>(m, "VectorVectorEl"); py::bind_vector<std::vector<std::vector<El>>>(m, "VectorVectorEl");
@ -82,11 +85,10 @@ TEST_SUBMODULE(stl_binders, m) {
// test_map_string_double_const // test_map_string_double_const
py::bind_map<std::map<std::string, double const>>(m, "MapStringDoubleConst"); 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") py::class_<E_nc>(m, "ENC").def(py::init<int>()).def_readwrite("value", &E_nc::value);
.def(py::init<int>())
.def_readwrite("value", &E_nc::value);
// test_noncopyable_containers // test_noncopyable_containers
py::bind_vector<std::vector<E_nc>>(m, "VectorENC"); py::bind_vector<std::vector<E_nc>>(m, "VectorENC");
@ -116,17 +118,31 @@ TEST_SUBMODULE(stl_binders, m) {
// test_vector_buffer // test_vector_buffer
py::bind_vector<std::vector<unsigned char>>(m, "VectorUChar", py::buffer_protocol()); py::bind_vector<std::vector<unsigned char>>(m, "VectorUChar", py::buffer_protocol());
// no dtype declared for this version: // no dtype declared for this version:
struct VUndeclStruct { bool w; uint32_t x; double y; bool z; }; struct VUndeclStruct {
m.def("create_undeclstruct", [m] () mutable { bool w;
py::bind_vector<std::vector<VUndeclStruct>>(m, "VectorUndeclStruct", py::buffer_protocol()); 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: // The rest depends on numpy:
try { py::module_::import("numpy"); } try {
catch (...) { return; } py::module_::import("numpy");
} catch (...) {
return;
}
// test_vector_buffer_numpy // 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); PYBIND11_NUMPY_DTYPE(VStruct, w, x, y, z);
py::class_<VStruct>(m, "VStruct").def_readwrite("x", &VStruct::x); py::class_<VStruct>(m, "VStruct").def_readwrite("x", &VStruct::x);
py::bind_vector<std::vector<VStruct>>(m, "VectorStruct", py::buffer_protocol()); py::bind_vector<std::vector<VStruct>>(m, "VectorStruct", py::buffer_protocol());

85
tests/test_tagbased_polymorphic.cpp

@ -7,11 +7,11 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include <pybind11/stl.h> #include <pybind11/stl.h>
struct Animal #include "pybind11_tests.h"
{
struct Animal {
// Make this type also a "standard" polymorphic type, to confirm that // Make this type also a "standard" polymorphic type, to confirm that
// specializing polymorphic_type_hook using enable_if_t still works // specializing polymorphic_type_hook using enable_if_t still works
// (https://github.com/pybind/pybind11/pull/2016/). // (https://github.com/pybind/pybind11/pull/2016/).
@ -20,57 +20,54 @@ struct Animal
// Enum for tag-based polymorphism. // Enum for tag-based polymorphism.
enum class Kind { enum class Kind {
Unknown = 0, Unknown = 0,
Dog = 100, Labrador, Chihuahua, LastDog = 199, Dog = 100,
Cat = 200, Panther, LastCat = 299 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); static std::string name_of_kind(Kind kind);
const Kind kind; const Kind kind;
const std::string name; const std::string name;
protected: protected:
Animal(const std::string& _name, Kind _kind) Animal(const std::string &_name, Kind _kind) : kind(_kind), name(_name) {}
: kind(_kind), name(_name)
{}
}; };
struct Dog : Animal struct Dog : Animal {
{
explicit Dog(const std::string &_name, Kind _kind = Kind::Dog) : Animal(_name, _kind) {} 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 bark() const { return name_of_kind(kind) + " " + name + " goes " + sound; }
std::string sound = "WOOF!"; std::string sound = "WOOF!";
}; };
struct Labrador : Dog struct Labrador : Dog {
{
explicit Labrador(const std::string &_name, int _excitement = 9001) explicit Labrador(const std::string &_name, int _excitement = 9001)
: Dog(_name, Kind::Labrador), excitement(_excitement) {} : Dog(_name, Kind::Labrador), excitement(_excitement) {}
int excitement; int excitement;
}; };
struct Chihuahua : Dog struct Chihuahua : Dog {
{
explicit Chihuahua(const std::string &_name) : Dog(_name, Kind::Chihuahua) { explicit Chihuahua(const std::string &_name) : Dog(_name, Kind::Chihuahua) {
sound = "iyiyiyiyiyi"; sound = "iyiyiyiyiyi";
} }
std::string bark() const { return Dog::bark() + " and runs in circles"; } 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) {} explicit Cat(const std::string &_name, Kind _kind = Kind::Cat) : Animal(_name, _kind) {}
std::string purr() const { return "mrowr"; } std::string purr() const { return "mrowr"; }
}; };
struct Panther : Cat struct Panther : Cat {
{
explicit Panther(const std::string &_name) : Cat(_name, Kind::Panther) {} explicit Panther(const std::string &_name) : Cat(_name, Kind::Panther) {}
std::string purr() const { return "mrrrRRRRRR"; } 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; std::vector<std::unique_ptr<Animal>> ret;
ret.emplace_back(new Labrador("Fido", 15000)); ret.emplace_back(new Labrador("Fido", 15000));
@ -85,19 +82,24 @@ std::vector<std::unique_ptr<Animal>> create_zoo()
return ret; return ret;
} }
const std::type_info* Animal::type_of_kind(Kind kind) const std::type_info *Animal::type_of_kind(Kind kind) {
{
switch (kind) { switch (kind) {
case Kind::Unknown: case Kind::Unknown:
case Kind::Dog: break; case Kind::Dog:
break;
case Kind::Labrador: return &typeid(Labrador); case Kind::Labrador:
case Kind::Chihuahua: return &typeid(Chihuahua); return &typeid(Labrador);
case Kind::Chihuahua:
return &typeid(Chihuahua);
case Kind::LastDog: case Kind::LastDog:
case Kind::Cat: break; case Kind::Cat:
case Kind::Panther: return &typeid(Panther); break;
case Kind::LastCat: break; case Kind::Panther:
return &typeid(Panther);
case Kind::LastCat:
break;
} }
if (kind >= Kind::Dog && kind <= Kind::LastDog) { if (kind >= Kind::Dog && kind <= Kind::LastDog) {
@ -109,25 +111,24 @@ const std::type_info* Animal::type_of_kind(Kind kind)
return nullptr; 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(); std::string raw_name = type_of_kind(kind)->name();
py::detail::clean_type_id(raw_name); py::detail::clean_type_id(raw_name);
return raw_name; return raw_name;
} }
namespace pybind11 { namespace pybind11 {
template <typename itype> template <typename itype>
struct polymorphic_type_hook<itype, detail::enable_if_t<std::is_base_of<Animal, itype>::value>> 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) {
static const void *get(const itype *src, const std::type_info*& type) type = src ? Animal::type_of_kind(src->kind) : nullptr;
{ type = src ? Animal::type_of_kind(src->kind) : nullptr; return src; } return src;
}; }
};
} // namespace pybind11 } // namespace pybind11
TEST_SUBMODULE(tagbased_polymorphic, m) { TEST_SUBMODULE(tagbased_polymorphic, m) {
py::class_<Animal>(m, "Animal") py::class_<Animal>(m, "Animal").def_readonly("name", &Animal::name);
.def_readonly("name", &Animal::name);
py::class_<Dog, Animal>(m, "Dog") py::class_<Dog, Animal>(m, "Dog")
.def(py::init<std::string>()) .def(py::init<std::string>())
.def_readwrite("sound", &Dog::sound) .def_readwrite("sound", &Dog::sound)
@ -138,9 +139,7 @@ TEST_SUBMODULE(tagbased_polymorphic, m) {
py::class_<Chihuahua, Dog>(m, "Chihuahua") py::class_<Chihuahua, Dog>(m, "Chihuahua")
.def(py::init<std::string>()) .def(py::init<std::string>())
.def("bark", &Chihuahua::bark); .def("bark", &Chihuahua::bark);
py::class_<Cat, Animal>(m, "Cat") py::class_<Cat, Animal>(m, "Cat").def(py::init<std::string>()).def("purr", &Cat::purr);
.def(py::init<std::string>())
.def("purr", &Cat::purr);
py::class_<Panther, Cat>(m, "Panther") py::class_<Panther, Cat>(m, "Panther")
.def(py::init<std::string>()) .def(py::init<std::string>())
.def("purr", &Panther::purr); .def("purr", &Panther::purr);

10
tests/test_thread.cpp

@ -10,20 +10,20 @@
#include <pybind11/cast.h> #include <pybind11/cast.h>
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include "pybind11_tests.h"
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include "pybind11_tests.h"
namespace py = pybind11; namespace py = pybind11;
namespace { namespace {
struct IntStruct { struct IntStruct {
explicit IntStruct(int v) : value(v) {}; explicit IntStruct(int v) : value(v){};
~IntStruct() { value = -value; } ~IntStruct() { value = -value; }
IntStruct(const IntStruct&) = default; IntStruct(const IntStruct &) = default;
IntStruct& operator=(const IntStruct&) = default; IntStruct &operator=(const IntStruct &) = default;
int value; int value;
}; };

278
tests/test_virtual_functions.cpp

@ -7,13 +7,15 @@
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
*/ */
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/functional.h> #include <pybind11/functional.h>
#include "constructor_stats.h"
#include "pybind11_tests.h"
#include <thread> #include <thread>
/* This is an example class that we'll want to be able to extend from Python */ /* This is an example class that we'll want to be able to extend from Python */
class ExampleVirt { class ExampleVirt {
public: public:
explicit ExampleVirt(int state) : state(state) { print_created(this, state); } explicit ExampleVirt(int state) : state(state) { print_created(this, state); }
ExampleVirt(const ExampleVirt &e) : state(e.state) { print_copy_created(this); } ExampleVirt(const ExampleVirt &e) : state(e.state) { print_copy_created(this); }
@ -25,7 +27,8 @@ public:
virtual int run(int value) { virtual int run(int value) {
py::print("Original implementation of " 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; return state + value;
} }
@ -33,8 +36,8 @@ public:
virtual void pure_virtual() = 0; virtual void pure_virtual() = 0;
// Returning a reference/pointer to a type converted from python (numbers, strings, etc.) is a // 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 // bit trickier, because the actual int& or std::string& or whatever only exists temporarily,
// we have to handle it specially in the trampoline class (see below). // 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_string1() { return str1; }
virtual const std::string *get_string2() { return &str2; } virtual const std::string *get_string2() { return &str2; }
@ -50,59 +53,53 @@ public:
int run(int value) override { int run(int value) override {
/* Generate wrapping code that enables native function overloading */ /* Generate wrapping code that enables native function overloading */
PYBIND11_OVERRIDE( PYBIND11_OVERRIDE(int, /* Return type */
int, /* Return type */ ExampleVirt, /* Parent class */
ExampleVirt, /* Parent class */ run, /* Name of function */
run, /* Name of function */ value /* Argument(s) */
value /* Argument(s) */
); );
} }
bool run_bool() override { bool run_bool() override {
PYBIND11_OVERRIDE_PURE( PYBIND11_OVERRIDE_PURE(bool, /* Return type */
bool, /* Return type */ ExampleVirt, /* Parent class */
ExampleVirt, /* Parent class */ run_bool, /* Name of function */
run_bool, /* Name of function */ /* This function has no arguments. The trailing comma
/* This function has no arguments. The trailing comma in the previous line is needed for some compilers */
in the previous line is needed for some compilers */
); );
} }
void pure_virtual() override { void pure_virtual() override {
PYBIND11_OVERRIDE_PURE( PYBIND11_OVERRIDE_PURE(void, /* Return type */
void, /* Return type */ ExampleVirt, /* Parent class */
ExampleVirt, /* Parent class */ pure_virtual, /* Name of function */
pure_virtual, /* Name of function */ /* This function has no arguments. The trailing comma
/* This function has no arguments. The trailing comma in the previous line is needed for some compilers */
in the previous line is needed for some compilers */
); );
} }
// We can return reference types for compatibility with C++ virtual interfaces that do so, but // We can return reference types for compatibility with C++ virtual interfaces that do so, but
// note they have some significant limitations (see the documentation). // note they have some significant limitations (see the documentation).
const std::string &get_string1() override { const std::string &get_string1() override {
PYBIND11_OVERRIDE( PYBIND11_OVERRIDE(const std::string &, /* Return type */
const std::string &, /* Return type */ ExampleVirt, /* Parent class */
ExampleVirt, /* Parent class */ get_string1, /* Name of function */
get_string1, /* Name of function */ /* (no arguments) */
/* (no arguments) */
); );
} }
const std::string *get_string2() override { const std::string *get_string2() override {
PYBIND11_OVERRIDE( PYBIND11_OVERRIDE(const std::string *, /* Return type */
const std::string *, /* Return type */ ExampleVirt, /* Parent class */
ExampleVirt, /* Parent class */ get_string2, /* Name of function */
get_string2, /* Name of function */ /* (no arguments) */
/* (no arguments) */
); );
} }
}; };
class NonCopyable { class NonCopyable {
public: 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(NonCopyable &&o) noexcept : value{std::move(o.value)} { print_move_created(this); }
NonCopyable(const NonCopyable &) = delete; NonCopyable(const NonCopyable &) = delete;
NonCopyable() = delete; NonCopyable() = delete;
@ -124,11 +121,12 @@ private:
// when it is not referenced elsewhere, but copied if it is still referenced. // when it is not referenced elsewhere, but copied if it is still referenced.
class Movable { class Movable {
public: 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(const Movable &m) : value{m.value} { print_copy_created(this); }
Movable(Movable &&m) noexcept : value{m.value} { print_move_created(this); } Movable(Movable &&m) noexcept : value{m.value} { print_move_created(this); }
std::string get_value() const { return std::to_string(value); } std::string get_value() const { return std::to_string(value); }
~Movable() { print_destroyed(this); } ~Movable() { print_destroyed(this); }
private: private:
int value; int value;
}; };
@ -137,7 +135,7 @@ class NCVirt {
public: public:
virtual ~NCVirt() = default; virtual ~NCVirt() = default;
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 NonCopyable get_noncopyable(int a, int b) { return NonCopyable(a, b); }
virtual Movable get_movable(int a, int b) = 0; virtual Movable get_movable(int a, int b) = 0;
@ -160,7 +158,7 @@ struct Base {
virtual std::string dispatch() const { return {}; }; virtual std::string dispatch() const { return {}; };
virtual ~Base() = default; virtual ~Base() = default;
Base() = default; Base() = default;
Base(const Base&) = delete; Base(const Base &) = delete;
}; };
struct DispatchIssue : Base { struct DispatchIssue : Base {
@ -173,33 +171,33 @@ struct DispatchIssue : Base {
// objects and send the result to the visitor functor // objects and send the result to the visitor functor
struct AdderBase { struct AdderBase {
struct Data {}; 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; virtual ~AdderBase() = default;
AdderBase() = default; AdderBase() = default;
AdderBase(const AdderBase&) = delete; AdderBase(const AdderBase &) = delete;
}; };
struct Adder : AdderBase { struct Adder : AdderBase {
void operator()(const Data& first, const Data& second, const DataVisitor& visitor) const override { void
PYBIND11_OVERRIDE_PURE_NAME(void, AdderBase, "__call__", operator(), first, second, visitor); 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() { static void test_gil() {
{ {
py::gil_scoped_acquire lock; py::gil_scoped_acquire lock;
py::print("1st lock acquired"); py::print("1st lock acquired");
} }
{ {
py::gil_scoped_acquire lock; py::gil_scoped_acquire lock;
py::print("2nd lock acquired"); py::print("2nd lock acquired");
} }
} }
static void test_gil_from_thread() { static void test_gil_from_thread() {
@ -225,12 +223,12 @@ class test_override_cache_helper_trampoline : public test_override_cache_helper
int func() override { PYBIND11_OVERRIDE(int, test_override_cache_helper, func); } 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 // Forward declaration (so that we can put the main tests here; the inherited virtual approaches
// rather long). // are rather long).
void initialize_inherited_virtuals(py::module_ &m); void initialize_inherited_virtuals(py::module_ &m);
TEST_SUBMODULE(virtual_functions, m) { TEST_SUBMODULE(virtual_functions, m) {
@ -242,11 +240,9 @@ TEST_SUBMODULE(virtual_functions, m) {
.def("run_bool", &ExampleVirt::run_bool) .def("run_bool", &ExampleVirt::run_bool)
.def("pure_virtual", &ExampleVirt::pure_virtual); .def("pure_virtual", &ExampleVirt::pure_virtual);
py::class_<NonCopyable>(m, "NonCopyable") py::class_<NonCopyable>(m, "NonCopyable").def(py::init<int, int>());
.def(py::init<int, int>());
py::class_<Movable>(m, "Movable") py::class_<Movable>(m, "Movable").def(py::init<int, int>());
.def(py::init<int, int>());
// test_move_support // test_move_support
#if !defined(__INTEL_COMPILER) && !defined(__CUDACC__) && !defined(__PGIC__) #if !defined(__INTEL_COMPILER) && !defined(__CUDACC__) && !defined(__PGIC__)
@ -259,7 +255,7 @@ TEST_SUBMODULE(virtual_functions, m) {
#endif #endif
m.def("runExampleVirt", [](ExampleVirt *ex, int value) { return ex->run(value); }); 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("runExampleVirtVirtual", [](ExampleVirt *ex) { ex->pure_virtual(); });
m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>); m.def("cstats_debug", &ConstructorStats::get<ExampleVirt>);
@ -270,27 +266,25 @@ TEST_SUBMODULE(virtual_functions, m) {
// that were not extended on the Python side // that were not extended on the Python side
struct A { struct A {
A() = default; A() = default;
A(const A&) = delete; A(const A &) = delete;
virtual ~A() = default; virtual ~A() = default;
virtual void f() { py::print("A.f()"); } virtual void f() { py::print("A.f()"); }
}; };
struct PyA : A { struct PyA : A {
PyA() { py::print("PyA.PyA()"); } PyA() { py::print("PyA.PyA()"); }
PyA(const PyA&) = delete; PyA(const PyA &) = delete;
~PyA() override { py::print("PyA.~PyA()"); } ~PyA() override { py::print("PyA.~PyA()"); }
void f() override { void f() override {
py::print("PyA.f()"); py::print("PyA.f()");
// This convolution just gives a `void`, but tests that PYBIND11_TYPE() works to protect // This convolution just gives a `void`, but tests that PYBIND11_TYPE() works to
// a type containing a , // protect a type containing a ,
PYBIND11_OVERRIDE(PYBIND11_TYPE(typename std::enable_if<true, void>::type), A, f); PYBIND11_OVERRIDE(PYBIND11_TYPE(typename std::enable_if<true, void>::type), A, f);
} }
}; };
py::class_<A, PyA>(m, "A") py::class_<A, PyA>(m, "A").def(py::init<>()).def("f", &A::f);
.def(py::init<>())
.def("f", &A::f);
m.def("call_f", [](A *a) { a->f(); }); m.def("call_f", [](A *a) { a->f(); });
@ -298,14 +292,14 @@ TEST_SUBMODULE(virtual_functions, m) {
// ... unless we explicitly request it, as in this example: // ... unless we explicitly request it, as in this example:
struct A2 { struct A2 {
A2() = default; A2() = default;
A2(const A2&) = delete; A2(const A2 &) = delete;
virtual ~A2() = default; virtual ~A2() = default;
virtual void f() { py::print("A2.f()"); } virtual void f() { py::print("A2.f()"); }
}; };
struct PyA2 : A2 { struct PyA2 : A2 {
PyA2() { py::print("PyA2.PyA2()"); } PyA2() { py::print("PyA2.PyA2()"); }
PyA2(const PyA2&) = delete; PyA2(const PyA2 &) = delete;
~PyA2() override { py::print("PyA2.~PyA2()"); } ~PyA2() override { py::print("PyA2.~PyA2()"); }
void f() override { void f() override {
py::print("PyA2.f()"); py::print("PyA2.f()");
@ -326,7 +320,7 @@ TEST_SUBMODULE(virtual_functions, m) {
.def(py::init<>()) .def(py::init<>())
.def("dispatch", &Base::dispatch); .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 // test_recursive_dispatch_issue
// #3357: Recursive dispatch fails to find python function override // #3357: Recursive dispatch fails to find python function override
@ -334,31 +328,39 @@ TEST_SUBMODULE(virtual_functions, m) {
.def(pybind11::init<>()) .def(pybind11::init<>())
.def("__call__", &AdderBase::operator()); .def("__call__", &AdderBase::operator());
pybind11::class_<AdderBase::Data>(m, "Data") pybind11::class_<AdderBase::Data>(m, "Data").def(pybind11::init<>());
.def(pybind11::init<>());
m.def("add2",
m.def("add2", [](const AdderBase::Data& first, const AdderBase::Data& second, [](const AdderBase::Data &first,
const AdderBase& adder, const AdderBase::DataVisitor& visitor) { const AdderBase::Data &second,
adder(first, second, visitor); 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, m.def("add3",
const AdderBase& adder, const AdderBase::DataVisitor& visitor) { [](const AdderBase::Data &first,
adder(first, second, [&] (const AdderBase::Data& first_plus_second) { const AdderBase::Data &second,
adder(first_plus_second, third, visitor); // NOLINT(readability-suspicious-call-argument) 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 // test_override_ref
// #392/397: overriding reference-returning functions // #392/397: overriding reference-returning functions
class OverrideTest { class OverrideTest {
public: public:
struct A { std::string value = "hi"; }; struct A {
std::string value = "hi";
};
std::string v; std::string v;
A a; A a;
explicit OverrideTest(const std::string &v) : v{v} {} explicit OverrideTest(const std::string &v) : v{v} {}
OverrideTest() = default; OverrideTest() = default;
OverrideTest(const OverrideTest&) = delete; OverrideTest(const OverrideTest &) = delete;
virtual std::string str_value() { return v; } virtual std::string str_value() { return v; }
virtual std::string &str_ref() { return v; } virtual std::string &str_ref() { return v; }
virtual A A_value() { return a; } virtual A A_value() { return a; }
@ -369,17 +371,22 @@ TEST_SUBMODULE(virtual_functions, m) {
class PyOverrideTest : public OverrideTest { class PyOverrideTest : public OverrideTest {
public: public:
using OverrideTest::OverrideTest; 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 // 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 // reference to a python numeric value, since we only copy values in the numeric type
// caster: // caster:
#ifdef PYBIND11_NEVER_DEFINED_EVER #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 #endif
// But we can work around it like this: // But we can work around it like this:
private: private:
std::string _tmp; std::string _tmp;
std::string str_ref_helper() { PYBIND11_OVERRIDE(std::string, OverrideTest, str_ref); } std::string str_ref_helper() { PYBIND11_OVERRIDE(std::string, OverrideTest, str_ref); }
public: public:
std::string &str_ref() override { return _tmp = str_ref_helper(); } std::string &str_ref() override { return _tmp = str_ref_helper(); }
@ -398,14 +405,15 @@ TEST_SUBMODULE(virtual_functions, m) {
.def("A_value", &OverrideTest::A_value) .def("A_value", &OverrideTest::A_value)
.def("A_ref", &OverrideTest::A_ref); .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(py::init_alias<>())
.def("func", &test_override_cache_helper::func); .def("func", &test_override_cache_helper::func);
m.def("test_override_cache", test_override_cache); m.def("test_override_cache", test_override_cache);
} }
// Inheriting virtual methods. We do two versions here: the repeat-everything version and the // Inheriting virtual methods. We do two versions here: the repeat-everything version and the
// templated trampoline versions mentioned in docs/advanced.rst. // templated trampoline versions mentioned in docs/advanced.rst.
// //
@ -414,83 +422,96 @@ TEST_SUBMODULE(virtual_functions, m) {
// properly (pybind11, sensibly, doesn't allow us to bind the same C++ class to // properly (pybind11, sensibly, doesn't allow us to bind the same C++ class to
// multiple python classes). // multiple python classes).
class A_Repeat { class A_Repeat {
#define A_METHODS \ #define A_METHODS \
public: \ public: \
virtual int unlucky_number() = 0; \ virtual int unlucky_number() = 0; \
virtual std::string say_something(unsigned times) { \ virtual std::string say_something(unsigned times) { \
std::string s = ""; \ std::string s = ""; \
for (unsigned i = 0; i < times; ++i) \ for (unsigned i = 0; i < times; ++i) \
s += "hi"; \ s += "hi"; \
return s; \ return s; \
} \ } \
std::string say_everything() { \ std::string say_everything() { \
return say_something(1) + " " + std::to_string(unlucky_number()); \ return say_something(1) + " " + std::to_string(unlucky_number()); \
} }
A_METHODS A_METHODS
A_Repeat() = default; A_Repeat() = default;
A_Repeat(const A_Repeat&) = delete; A_Repeat(const A_Repeat &) = delete;
virtual ~A_Repeat() = default; virtual ~A_Repeat() = default;
}; };
class B_Repeat : public A_Repeat { class B_Repeat : public A_Repeat {
#define B_METHODS \ #define B_METHODS \
public: \ public: \
int unlucky_number() override { return 13; } \ int unlucky_number() override { return 13; } \
std::string say_something(unsigned times) override { \ std::string say_something(unsigned times) override { \
return "B says hi " + std::to_string(times) + " times"; \ return "B says hi " + std::to_string(times) + " times"; \
} \ } \
virtual double lucky_number() { return 7.0; } virtual double lucky_number() { return 7.0; }
B_METHODS B_METHODS
}; };
class C_Repeat : public B_Repeat { class C_Repeat : public B_Repeat {
#define C_METHODS \ #define C_METHODS \
public: \ public: \
int unlucky_number() override { return 4444; } \ int unlucky_number() override { return 4444; } \
double lucky_number() override { return 888; } double lucky_number() override { return 888; }
C_METHODS C_METHODS
}; };
class D_Repeat : public C_Repeat { class D_Repeat : public C_Repeat {
#define D_METHODS // Nothing overridden. #define D_METHODS // Nothing overridden.
D_METHODS D_METHODS
}; };
// Base classes for templated inheritance trampolines. Identical to the repeat-everything version: // Base classes for templated inheritance trampolines. Identical to the repeat-everything version:
class A_Tpl { class A_Tpl {
A_METHODS; A_METHODS;
A_Tpl() = default; A_Tpl() = default;
A_Tpl(const A_Tpl&) = delete; A_Tpl(const A_Tpl &) = delete;
virtual ~A_Tpl() = default; virtual ~A_Tpl() = default;
}; };
class B_Tpl : public A_Tpl { B_METHODS }; class B_Tpl : public A_Tpl {
class C_Tpl : public B_Tpl { C_METHODS }; B_METHODS
class D_Tpl : public C_Tpl { D_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) // Inheritance approach 1: each trampoline gets every virtual method (11 in total)
class PyA_Repeat : public A_Repeat { class PyA_Repeat : public A_Repeat {
public: public:
using A_Repeat::A_Repeat; using A_Repeat::A_Repeat;
int unlucky_number() override { PYBIND11_OVERRIDE_PURE(int, A_Repeat, unlucky_number, ); } 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 { class PyB_Repeat : public B_Repeat {
public: public:
using B_Repeat::B_Repeat; using B_Repeat::B_Repeat;
int unlucky_number() override { PYBIND11_OVERRIDE(int, B_Repeat, unlucky_number, ); } 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, ); } double lucky_number() override { PYBIND11_OVERRIDE(double, B_Repeat, lucky_number, ); }
}; };
class PyC_Repeat : public C_Repeat { class PyC_Repeat : public C_Repeat {
public: public:
using C_Repeat::C_Repeat; using C_Repeat::C_Repeat;
int unlucky_number() override { PYBIND11_OVERRIDE(int, C_Repeat, unlucky_number, ); } 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, ); } double lucky_number() override { PYBIND11_OVERRIDE(double, C_Repeat, lucky_number, ); }
}; };
class PyD_Repeat : public D_Repeat { class PyD_Repeat : public D_Repeat {
public: public:
using D_Repeat::D_Repeat; using D_Repeat::D_Repeat;
int unlucky_number() override { PYBIND11_OVERRIDE(int, D_Repeat, unlucky_number, ); } 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, ); } double lucky_number() override { PYBIND11_OVERRIDE(double, D_Repeat, lucky_number, ); }
}; };
@ -513,7 +534,9 @@ class PyA_Tpl : public Base {
public: public:
using Base::Base; // Inherit constructors using Base::Base; // Inherit constructors
int unlucky_number() override { PYBIND11_OVERRIDE_PURE(int, Base, unlucky_number, ); } 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> template <class Base = B_Tpl>
class PyB_Tpl : public PyA_Tpl<Base> { class PyB_Tpl : public PyA_Tpl<Base> {
@ -548,10 +571,8 @@ void initialize_inherited_virtuals(py::module_ &m) {
py::class_<B_Repeat, A_Repeat, PyB_Repeat>(m, "B_Repeat") py::class_<B_Repeat, A_Repeat, PyB_Repeat>(m, "B_Repeat")
.def(py::init<>()) .def(py::init<>())
.def("lucky_number", &B_Repeat::lucky_number); .def("lucky_number", &B_Repeat::lucky_number);
py::class_<C_Repeat, B_Repeat, PyC_Repeat>(m, "C_Repeat") py::class_<C_Repeat, B_Repeat, PyC_Repeat>(m, "C_Repeat").def(py::init<>());
.def(py::init<>()); py::class_<D_Repeat, C_Repeat, PyD_Repeat>(m, "D_Repeat").def(py::init<>());
py::class_<D_Repeat, C_Repeat, PyD_Repeat>(m, "D_Repeat")
.def(py::init<>());
// test_ // test_
// Method 2: Templated trampolines // Method 2: Templated trampolines
@ -563,11 +584,8 @@ void initialize_inherited_virtuals(py::module_ &m) {
py::class_<B_Tpl, A_Tpl, PyB_Tpl<>>(m, "B_Tpl") py::class_<B_Tpl, A_Tpl, PyB_Tpl<>>(m, "B_Tpl")
.def(py::init<>()) .def(py::init<>())
.def("lucky_number", &B_Tpl::lucky_number); .def("lucky_number", &B_Tpl::lucky_number);
py::class_<C_Tpl, B_Tpl, PyB_Tpl<C_Tpl>>(m, "C_Tpl") py::class_<C_Tpl, B_Tpl, PyB_Tpl<C_Tpl>>(m, "C_Tpl").def(py::init<>());
.def(py::init<>()); py::class_<D_Tpl, C_Tpl, PyB_Tpl<D_Tpl>>(m, "D_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) // Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
m.def("test_gil", &test_gil); m.def("test_gil", &test_gil);

Loading…
Cancel
Save