Allow arbitrary class_ template option ordering
The current pybind11::class_<Type, Holder, Trampoline> fixed template
ordering results in a requirement to repeat the Holder with its default
value (std::unique_ptr<Type>) argument, which is a little bit annoying:
it needs to be specified not because we want to override the default,
but rather because we need to specify the third argument.
This commit removes this limitation by making the class_ template take
the type name plus a parameter pack of options. It then extracts the
first valid holder type and the first subclass type for holder_type and
trampoline type_alias, respectively. (If unfound, both fall back to
their current defaults, `std::unique_ptr<type>` and `type`,
respectively). If any unmatched template arguments are provided, a
static assertion fails.
What this means is that you can specify or omit the arguments in any
order:
py::class_<A, PyA> c1(m, "A");
py::class_<B, PyB, std::shared_ptr<B>> c2(m, "B");
py::class_<C, std::shared_ptr<C>, PyB> c3(m, "C");
It also allows future class attributes (such as base types in the next
commit) to be passed as class template types rather than needing to use
a py::base<> wrapper.
2016-09-06 16:17:06 +00:00
|
|
|
/*
|
2017-06-08 22:44:49 +00:00
|
|
|
tests/test_class.cpp -- test py::class_ definitions and basic functionality
|
Allow arbitrary class_ template option ordering
The current pybind11::class_<Type, Holder, Trampoline> fixed template
ordering results in a requirement to repeat the Holder with its default
value (std::unique_ptr<Type>) argument, which is a little bit annoying:
it needs to be specified not because we want to override the default,
but rather because we need to specify the third argument.
This commit removes this limitation by making the class_ template take
the type name plus a parameter pack of options. It then extracts the
first valid holder type and the first subclass type for holder_type and
trampoline type_alias, respectively. (If unfound, both fall back to
their current defaults, `std::unique_ptr<type>` and `type`,
respectively). If any unmatched template arguments are provided, a
static assertion fails.
What this means is that you can specify or omit the arguments in any
order:
py::class_<A, PyA> c1(m, "A");
py::class_<B, PyB, std::shared_ptr<B>> c2(m, "B");
py::class_<C, std::shared_ptr<C>, PyB> c3(m, "C");
It also allows future class attributes (such as base types in the next
commit) to be passed as class template types rather than needing to use
a py::base<> wrapper.
2016-09-06 16:17:06 +00:00
|
|
|
|
|
|
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
|
|
|
|
|
|
|
All rights reserved. Use of this source code is governed by a
|
|
|
|
BSD-style license that can be found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
fix: Intel ICC C++17 compatibility (#2729)
* CI: Intel icc/icpc via oneAPI
Add testing for Intel icc/icpc via the oneAPI images.
Intel oneAPI is in a late beta stage, currently shipping
oneAPI beta09 with ICC 20.2.
CI: Skip Interpreter Tests for Intel
Cannot find how to add this, neiter the package `libc6-dev` nor
`intel-oneapi-mkl-devel` help when installed to solve this:
```
-- Looking for C++ include pthread.h
-- Looking for C++ include pthread.h - not found
CMake Error at /__t/cmake/3.18.4/x64/cmake-3.18.4-Linux-x86_64/share/cmake-3.18/Modules/FindPackageHandleStandardArgs.cmake:165 (message):
Could NOT find Threads (missing: Threads_FOUND)
Call Stack (most recent call first):
/__t/cmake/3.18.4/x64/cmake-3.18.4-Linux-x86_64/share/cmake-3.18/Modules/FindPackageHandleStandardArgs.cmake:458 (_FPHSA_FAILURE_MESSAGE)
/__t/cmake/3.18.4/x64/cmake-3.18.4-Linux-x86_64/share/cmake-3.18/Modules/FindThreads.cmake:234 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
tests/test_embed/CMakeLists.txt:17 (find_package)
```
CI: libc6-dev from GCC for ICC
CI: Run bare metal for oneAPI
CI: Ubuntu 18.04 for oneAPI
CI: Intel +Catch -Eigen
CI: CMake from Apt (ICC tests)
CI: Replace Intel Py with GCC Py
CI: Intel w/o GCC's Eigen
CI: ICC with verbose make
[Debug] Find core dump
tests: use arg{} instead of arg() for Intel
tests: adding a few more missing {}
fix: sync with @tobiasleibner's branch
fix: try ubuntu 20-04
fix: drop exit 1
docs: Apply suggestions from code review
Co-authored-by: Tobias Leibner <tobias.leibner@googlemail.com>
Workaround for ICC enable_if issues
Another workaround for ICC's enable_if issues
fix error in previous commit
Disable one test for the Intel compiler in C++17 mode
Add back one instance of py::arg().noconvert()
Add NOLINT to fix clang-tidy check
Work around for ICC internal error in PYBIND11_EXPAND_SIDE_EFFECTS in C++17 mode
CI: Intel ICC with C++17
docs: pybind11/numpy.h does not require numpy at build time. (#2720)
This is nice enough to be mentioned explicitly in the docs.
docs: Update warning about Python 3.9.0 UB, now that 3.9.1 has been released (#2719)
Adjusting `type_caster<std::reference_wrapper<T>>` to support const/non-const propagation in `cast_op`. (#2705)
* Allow type_caster of std::reference_wrapper<T> to be the same as a native reference.
Before, both std::reference_wrapper<T> and std::reference_wrapper<const T> would
invoke cast_op<type>. This doesn't allow the type_caster<> specialization for T
to distinguish reference_wrapper types from value types.
After, the type_caster<> specialization invokes cast_op<type&>, which allows
reference_wrapper to behave in the same way as a native reference type.
* Add tests/examples for std::reference_wrapper<const T>
* Add tests which use mutable/immutable variants
This test is a chimera; it blends the pybind11 casters with a custom
pytype implementation that supports immutable and mutable calls.
In order to detect the immutable/mutable state, the cast_op needs
to propagate it, even through e.g. std::reference<const T>
Note: This is still a work in progress; some things are crashing,
which likely means that I have a refcounting bug or something else
missing.
* Add/finish tests that distinguish const& from &
Fixes the bugs in my custom python type implementation,
demonstrate test that requires const& and reference_wrapper<const T>
being treated differently from Non-const.
* Add passing a const to non-const method.
* Demonstrate non-const conversion of reference_wrapper in tests.
Apply formatting presubmit check.
* Fix build errors from presubmit checks.
* Try and fix a few more CI errors
* More CI fixes.
* More CI fixups.
* Try and get PyPy to work.
* Additional minor fixups. Getting close to CI green.
* More ci fixes?
* fix clang-tidy warnings from presubmit
* fix more clang-tidy warnings
* minor comment and consistency cleanups
* PyDECREF -> Py_DECREF
* copy/move constructors
* Resolve codereview comments
* more review comment fixes
* review comments: remove spurious &
* Make the test fail even when the static_assert is commented out.
This expands the test_freezable_type_caster a bit by:
1/ adding accessors .is_immutable and .addr to compare identity
from python.
2/ Changing the default cast_op of the type_caster<> specialization
to return a non-const value. In normal codepaths this is a reasonable
default.
3/ adding roundtrip variants to exercise the by reference, by pointer
and by reference_wrapper in all call paths. In conjunction with 2/, this
demonstrates the failure case of the existing std::reference_wrpper conversion,
which now loses const in a similar way that happens when using the default cast_op_type<>.
* apply presubmit formatting
* Revert inclusion of test_freezable_type_caster
There's some concern that this test is a bit unwieldly because of the use
of the raw <Python.h> functions. Removing for now.
* Add a test that validates const references propagation.
This test verifies that cast_op may be used to correctly detect
const reference types when used with std::reference_wrapper.
* mend
* Review comments based changes.
1. std::add_lvalue_reference<type> -> type&
2. Simplify the test a little more; we're never returning the ConstRefCaster
type so the class_ definition can be removed.
* formatted files again.
* Move const_ref_caster test to builtin_casters
* Review comments: use cast_op and adjust some comments.
* Simplify ConstRefCasted test
I like this version better as it moves the assertion that matters
back into python.
ci: drop pypy2 linux, PGI 20.7, add Python 10 dev (#2724)
* ci: drop pypy2 linux, add Python 10 dev
* ci: fix mistake
* ci: commented-out PGI 20.11, drop 20.7
fix: regression with installed pybind11 overriding local one (#2716)
* fix: regression with installed pybind11 overriding discovered one
Closes #2709
* docs: wording incorrect
style: remove redundant instance->owned = true (#2723)
which was just before set to True in instance->allocate_layout()
fix: also throw in the move-constructor added by the PYBIND11_OBJECT macro, after the argument has been moved-out (if necessary) (#2701)
Make args_are_all_* ICC workarounds unconditional
Disable test_aligned on Intel ICC
Fix test_aligned on Intel ICC
Skip test_python_alreadyset_in_destructor on Intel ICC
Fix test_aligned again
ICC CI: Downgrade pytest
pytest 6 does not capture the `discard_as_unraisable` stderr and
just writes a warning with its content instead.
* refactor: simpler Intel workaround, suggested by @laramiel
* fix: try version with impl to see if it is easier to compile
* docs: update README for ICC
Co-authored-by: Axel Huebl <axel.huebl@plasma.ninja>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
2021-01-18 00:53:07 +00:00
|
|
|
#if defined(__INTEL_COMPILER) && __cplusplus >= 201703L
|
|
|
|
// Intel compiler requires a separate header file to support aligned new operators
|
|
|
|
// and does not set the __cpp_aligned_new feature macro.
|
|
|
|
// This header needs to be included before pybind11.
|
|
|
|
#include <aligned_new>
|
|
|
|
#endif
|
|
|
|
|
Allow arbitrary class_ template option ordering
The current pybind11::class_<Type, Holder, Trampoline> fixed template
ordering results in a requirement to repeat the Holder with its default
value (std::unique_ptr<Type>) argument, which is a little bit annoying:
it needs to be specified not because we want to override the default,
but rather because we need to specify the third argument.
This commit removes this limitation by making the class_ template take
the type name plus a parameter pack of options. It then extracts the
first valid holder type and the first subclass type for holder_type and
trampoline type_alias, respectively. (If unfound, both fall back to
their current defaults, `std::unique_ptr<type>` and `type`,
respectively). If any unmatched template arguments are provided, a
static assertion fails.
What this means is that you can specify or omit the arguments in any
order:
py::class_<A, PyA> c1(m, "A");
py::class_<B, PyB, std::shared_ptr<B>> c2(m, "B");
py::class_<C, std::shared_ptr<C>, PyB> c3(m, "C");
It also allows future class attributes (such as base types in the next
commit) to be passed as class template types rather than needing to use
a py::base<> wrapper.
2016-09-06 16:17:06 +00:00
|
|
|
#include "pybind11_tests.h"
|
2017-06-08 22:44:49 +00:00
|
|
|
#include "constructor_stats.h"
|
2017-07-29 02:03:44 +00:00
|
|
|
#include "local_bindings.h"
|
2018-01-11 17:22:13 +00:00
|
|
|
#include <pybind11/stl.h>
|
|
|
|
|
2018-11-09 19:14:53 +00:00
|
|
|
#if defined(_MSC_VER)
|
|
|
|
# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier
|
|
|
|
#endif
|
|
|
|
|
2018-01-11 17:22:13 +00:00
|
|
|
// test_brace_initialization
|
|
|
|
struct NoBraceInitialization {
|
|
|
|
NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
|
|
|
|
template <typename T>
|
|
|
|
NoBraceInitialization(std::initializer_list<T> l) : vec(l) {}
|
|
|
|
|
|
|
|
std::vector<int> vec;
|
|
|
|
};
|
Allow arbitrary class_ template option ordering
The current pybind11::class_<Type, Holder, Trampoline> fixed template
ordering results in a requirement to repeat the Holder with its default
value (std::unique_ptr<Type>) argument, which is a little bit annoying:
it needs to be specified not because we want to override the default,
but rather because we need to specify the third argument.
This commit removes this limitation by making the class_ template take
the type name plus a parameter pack of options. It then extracts the
first valid holder type and the first subclass type for holder_type and
trampoline type_alias, respectively. (If unfound, both fall back to
their current defaults, `std::unique_ptr<type>` and `type`,
respectively). If any unmatched template arguments are provided, a
static assertion fails.
What this means is that you can specify or omit the arguments in any
order:
py::class_<A, PyA> c1(m, "A");
py::class_<B, PyB, std::shared_ptr<B>> c2(m, "B");
py::class_<C, std::shared_ptr<C>, PyB> c3(m, "C");
It also allows future class attributes (such as base types in the next
commit) to be passed as class template types rather than needing to use
a py::base<> wrapper.
2016-09-06 16:17:06 +00:00
|
|
|
|
2017-06-08 22:44:49 +00:00
|
|
|
TEST_SUBMODULE(class_, m) {
|
|
|
|
// test_instance
|
|
|
|
struct NoConstructor {
|
2017-11-20 13:19:53 +00:00
|
|
|
NoConstructor() = default;
|
|
|
|
NoConstructor(const NoConstructor &) = default;
|
|
|
|
NoConstructor(NoConstructor &&) = default;
|
2017-06-08 22:44:49 +00:00
|
|
|
static NoConstructor *new_instance() {
|
|
|
|
auto *ptr = new NoConstructor();
|
|
|
|
print_created(ptr, "via new_instance");
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
~NoConstructor() { print_destroyed(this); }
|
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<NoConstructor>(m, "NoConstructor")
|
|
|
|
.def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
|
2017-06-22 21:42:11 +00:00
|
|
|
|
|
|
|
// test_inheritance
|
|
|
|
class Pet {
|
|
|
|
public:
|
|
|
|
Pet(const std::string &name, const std::string &species)
|
|
|
|
: m_name(name), m_species(species) {}
|
|
|
|
std::string name() const { return m_name; }
|
|
|
|
std::string species() const { return m_species; }
|
|
|
|
private:
|
|
|
|
std::string m_name;
|
|
|
|
std::string m_species;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Dog : public Pet {
|
|
|
|
public:
|
|
|
|
Dog(const std::string &name) : Pet(name, "dog") {}
|
|
|
|
std::string bark() const { return "Woof!"; }
|
|
|
|
};
|
|
|
|
|
|
|
|
class Rabbit : public Pet {
|
|
|
|
public:
|
|
|
|
Rabbit(const std::string &name) : Pet(name, "parrot") {}
|
|
|
|
};
|
|
|
|
|
|
|
|
class Hamster : public Pet {
|
|
|
|
public:
|
|
|
|
Hamster(const std::string &name) : Pet(name, "rodent") {}
|
|
|
|
};
|
|
|
|
|
|
|
|
class Chimera : public Pet {
|
|
|
|
Chimera() : Pet("Kimmy", "chimera") {}
|
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<Pet> pet_class(m, "Pet");
|
|
|
|
pet_class
|
|
|
|
.def(py::init<std::string, std::string>())
|
|
|
|
.def("name", &Pet::name)
|
|
|
|
.def("species", &Pet::species);
|
|
|
|
|
|
|
|
/* One way of declaring a subclass relationship: reference parent's class_ object */
|
|
|
|
py::class_<Dog>(m, "Dog", pet_class)
|
|
|
|
.def(py::init<std::string>());
|
|
|
|
|
|
|
|
/* Another way of declaring a subclass relationship: reference parent's C++ type */
|
|
|
|
py::class_<Rabbit, Pet>(m, "Rabbit")
|
|
|
|
.def(py::init<std::string>());
|
|
|
|
|
|
|
|
/* And another: list parent in class template arguments */
|
|
|
|
py::class_<Hamster, Pet>(m, "Hamster")
|
|
|
|
.def(py::init<std::string>());
|
|
|
|
|
|
|
|
/* Constructors are not inherited by default */
|
|
|
|
py::class_<Chimera, Pet>(m, "Chimera");
|
|
|
|
|
|
|
|
m.def("pet_name_species", [](const Pet &pet) { return pet.name() + " is a " + pet.species(); });
|
|
|
|
m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
|
|
|
|
|
|
|
|
// test_automatic_upcasting
|
2017-11-20 13:19:53 +00:00
|
|
|
struct BaseClass {
|
|
|
|
BaseClass() = default;
|
|
|
|
BaseClass(const BaseClass &) = default;
|
|
|
|
BaseClass(BaseClass &&) = default;
|
2020-09-11 03:15:22 +00:00
|
|
|
virtual ~BaseClass() = default;
|
2017-11-20 13:19:53 +00:00
|
|
|
};
|
2017-06-22 21:42:11 +00:00
|
|
|
struct DerivedClass1 : BaseClass { };
|
|
|
|
struct DerivedClass2 : BaseClass { };
|
|
|
|
|
|
|
|
py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
|
|
|
|
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
|
|
|
|
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
|
|
|
|
|
|
|
|
m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); });
|
|
|
|
m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); });
|
|
|
|
m.def("return_class_n", [](int n) -> BaseClass* {
|
|
|
|
if (n == 1) return new DerivedClass1();
|
|
|
|
if (n == 2) return new DerivedClass2();
|
|
|
|
return new BaseClass();
|
|
|
|
});
|
|
|
|
m.def("return_none", []() -> BaseClass* { return nullptr; });
|
|
|
|
|
|
|
|
// test_isinstance
|
|
|
|
m.def("check_instances", [](py::list l) {
|
|
|
|
return py::make_tuple(
|
|
|
|
py::isinstance<py::tuple>(l[0]),
|
|
|
|
py::isinstance<py::dict>(l[1]),
|
|
|
|
py::isinstance<Pet>(l[2]),
|
|
|
|
py::isinstance<Pet>(l[3]),
|
|
|
|
py::isinstance<Dog>(l[4]),
|
|
|
|
py::isinstance<Rabbit>(l[5]),
|
|
|
|
py::isinstance<UnregisteredType>(l[6])
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-09-14 22:06:26 +00:00
|
|
|
struct Invalid {};
|
|
|
|
|
|
|
|
// test_type
|
|
|
|
m.def("check_type", [](int category) {
|
|
|
|
// Currently not supported (via a fail at compile time)
|
|
|
|
// See https://github.com/pybind/pybind11/issues/2486
|
|
|
|
// if (category == 2)
|
|
|
|
// return py::type::of<int>();
|
|
|
|
if (category == 1)
|
|
|
|
return py::type::of<DerivedClass1>();
|
|
|
|
else
|
|
|
|
return py::type::of<Invalid>();
|
|
|
|
});
|
|
|
|
|
|
|
|
m.def("get_type_of", [](py::object ob) {
|
|
|
|
return py::type::of(ob);
|
|
|
|
});
|
|
|
|
|
2020-09-17 02:02:09 +00:00
|
|
|
m.def("get_type_classic", [](py::handle h) {
|
|
|
|
return h.get_type();
|
|
|
|
});
|
|
|
|
|
2020-09-14 22:06:26 +00:00
|
|
|
m.def("as_type", [](py::object ob) {
|
2020-10-05 20:48:54 +00:00
|
|
|
return py::type(ob);
|
2020-09-14 22:06:26 +00:00
|
|
|
});
|
|
|
|
|
2017-06-22 21:42:11 +00:00
|
|
|
// test_mismatched_holder
|
|
|
|
struct MismatchBase1 { };
|
|
|
|
struct MismatchDerived1 : MismatchBase1 { };
|
|
|
|
|
|
|
|
struct MismatchBase2 { };
|
|
|
|
struct MismatchDerived2 : MismatchBase2 { };
|
|
|
|
|
|
|
|
m.def("mismatched_holder_1", []() {
|
2020-10-03 17:38:03 +00:00
|
|
|
auto mod = py::module_::import("__main__");
|
2017-06-22 21:42:11 +00:00
|
|
|
py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(mod, "MismatchBase1");
|
|
|
|
py::class_<MismatchDerived1, MismatchBase1>(mod, "MismatchDerived1");
|
|
|
|
});
|
|
|
|
m.def("mismatched_holder_2", []() {
|
2020-10-03 17:38:03 +00:00
|
|
|
auto mod = py::module_::import("__main__");
|
2017-06-22 21:42:11 +00:00
|
|
|
py::class_<MismatchBase2>(mod, "MismatchBase2");
|
|
|
|
py::class_<MismatchDerived2, std::shared_ptr<MismatchDerived2>,
|
|
|
|
MismatchBase2>(mod, "MismatchDerived2");
|
|
|
|
});
|
|
|
|
|
|
|
|
// test_override_static
|
|
|
|
// #511: problem with inheritance + overwritten def_static
|
|
|
|
struct MyBase {
|
|
|
|
static std::unique_ptr<MyBase> make() {
|
|
|
|
return std::unique_ptr<MyBase>(new MyBase());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MyDerived : MyBase {
|
|
|
|
static std::unique_ptr<MyDerived> make() {
|
|
|
|
return std::unique_ptr<MyDerived>(new MyDerived());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<MyBase>(m, "MyBase")
|
|
|
|
.def_static("make", &MyBase::make);
|
|
|
|
|
|
|
|
py::class_<MyDerived, MyBase>(m, "MyDerived")
|
|
|
|
.def_static("make", &MyDerived::make)
|
|
|
|
.def_static("make2", &MyDerived::make);
|
2017-06-26 18:34:06 +00:00
|
|
|
|
|
|
|
// test_implicit_conversion_life_support
|
|
|
|
struct ConvertibleFromUserType {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ConvertibleFromUserType(UserType u) : i(u.value()) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<ConvertibleFromUserType>(m, "AcceptsUserType")
|
|
|
|
.def(py::init<UserType>());
|
|
|
|
py::implicitly_convertible<UserType, ConvertibleFromUserType>();
|
|
|
|
|
|
|
|
m.def("implicitly_convert_argument", [](const ConvertibleFromUserType &r) { return r.i; });
|
|
|
|
m.def("implicitly_convert_variable", [](py::object o) {
|
|
|
|
// `o` is `UserType` and `r` is a reference to a temporary created by implicit
|
|
|
|
// conversion. This is valid when called inside a bound function because the temp
|
|
|
|
// object is attached to the same life support system as the arguments.
|
|
|
|
const auto &r = o.cast<const ConvertibleFromUserType &>();
|
|
|
|
return r.i;
|
|
|
|
});
|
|
|
|
m.add_object("implicitly_convert_variable_fail", [&] {
|
|
|
|
auto f = [](PyObject *, PyObject *args) -> PyObject * {
|
|
|
|
auto o = py::reinterpret_borrow<py::tuple>(args)[0];
|
|
|
|
try { // It should fail here because there is no life support.
|
|
|
|
o.cast<const ConvertibleFromUserType &>();
|
|
|
|
} catch (const py::cast_error &e) {
|
|
|
|
return py::str(e.what()).release().ptr();
|
|
|
|
}
|
|
|
|
return py::str().release().ptr();
|
|
|
|
};
|
|
|
|
|
|
|
|
auto def = new PyMethodDef{"f", f, METH_VARARGS, nullptr};
|
2021-01-01 16:05:22 +00:00
|
|
|
py::capsule def_capsule(def, [](void *ptr) { delete reinterpret_cast<PyMethodDef *>(ptr); });
|
|
|
|
return py::reinterpret_steal<py::object>(PyCFunction_NewEx(def, def_capsule.ptr(), m.ptr()));
|
2017-06-26 18:34:06 +00:00
|
|
|
}());
|
2017-07-23 04:32:58 +00:00
|
|
|
|
|
|
|
// test_operator_new_delete
|
|
|
|
struct HasOpNewDel {
|
|
|
|
std::uint64_t i;
|
|
|
|
static void *operator new(size_t s) { py::print("A new", s); return ::operator new(s); }
|
|
|
|
static void *operator new(size_t s, void *ptr) { py::print("A placement-new", s); return ptr; }
|
|
|
|
static void operator delete(void *p) { py::print("A delete"); return ::operator delete(p); }
|
|
|
|
};
|
|
|
|
struct HasOpNewDelSize {
|
|
|
|
std::uint32_t i;
|
|
|
|
static void *operator new(size_t s) { py::print("B new", s); return ::operator new(s); }
|
|
|
|
static void *operator new(size_t s, void *ptr) { py::print("B placement-new", s); return ptr; }
|
|
|
|
static void operator delete(void *p, size_t s) { py::print("B delete", s); return ::operator delete(p); }
|
|
|
|
};
|
|
|
|
struct AliasedHasOpNewDelSize {
|
|
|
|
std::uint64_t i;
|
|
|
|
static void *operator new(size_t s) { py::print("C new", s); return ::operator new(s); }
|
|
|
|
static void *operator new(size_t s, void *ptr) { py::print("C placement-new", s); return ptr; }
|
|
|
|
static void operator delete(void *p, size_t s) { py::print("C delete", s); return ::operator delete(p); }
|
|
|
|
virtual ~AliasedHasOpNewDelSize() = default;
|
2020-07-24 01:16:54 +00:00
|
|
|
AliasedHasOpNewDelSize() = default;
|
|
|
|
AliasedHasOpNewDelSize(const AliasedHasOpNewDelSize&) = delete;
|
2017-07-23 04:32:58 +00:00
|
|
|
};
|
|
|
|
struct PyAliasedHasOpNewDelSize : AliasedHasOpNewDelSize {
|
|
|
|
PyAliasedHasOpNewDelSize() = default;
|
|
|
|
PyAliasedHasOpNewDelSize(int) { }
|
|
|
|
std::uint64_t j;
|
|
|
|
};
|
|
|
|
struct HasOpNewDelBoth {
|
|
|
|
std::uint32_t i[8];
|
|
|
|
static void *operator new(size_t s) { py::print("D new", s); return ::operator new(s); }
|
|
|
|
static void *operator new(size_t s, void *ptr) { py::print("D placement-new", s); return ptr; }
|
|
|
|
static void operator delete(void *p) { py::print("D delete"); return ::operator delete(p); }
|
|
|
|
static void operator delete(void *p, size_t s) { py::print("D wrong delete", s); return ::operator delete(p); }
|
|
|
|
};
|
|
|
|
py::class_<HasOpNewDel>(m, "HasOpNewDel").def(py::init<>());
|
|
|
|
py::class_<HasOpNewDelSize>(m, "HasOpNewDelSize").def(py::init<>());
|
|
|
|
py::class_<HasOpNewDelBoth>(m, "HasOpNewDelBoth").def(py::init<>());
|
|
|
|
py::class_<AliasedHasOpNewDelSize, PyAliasedHasOpNewDelSize> aliased(m, "AliasedHasOpNewDelSize");
|
|
|
|
aliased.def(py::init<>());
|
|
|
|
aliased.attr("size_noalias") = py::int_(sizeof(AliasedHasOpNewDelSize));
|
|
|
|
aliased.attr("size_alias") = py::int_(sizeof(PyAliasedHasOpNewDelSize));
|
2017-07-29 02:03:44 +00:00
|
|
|
|
|
|
|
// This test is actually part of test_local_bindings (test_duplicate_local), but we need a
|
|
|
|
// definition in a different compilation unit within the same module:
|
|
|
|
bind_local<LocalExternal, 17>(m, "LocalExternal", py::module_local());
|
2017-08-17 15:03:46 +00:00
|
|
|
|
|
|
|
// test_bind_protected_functions
|
|
|
|
class ProtectedA {
|
|
|
|
protected:
|
|
|
|
int foo() const { return value; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
int value = 42;
|
|
|
|
};
|
|
|
|
|
|
|
|
class PublicistA : public ProtectedA {
|
|
|
|
public:
|
|
|
|
using ProtectedA::foo;
|
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<ProtectedA>(m, "ProtectedA")
|
|
|
|
.def(py::init<>())
|
|
|
|
#if !defined(_MSC_VER) || _MSC_VER >= 1910
|
|
|
|
.def("foo", &PublicistA::foo);
|
|
|
|
#else
|
|
|
|
.def("foo", static_cast<int (ProtectedA::*)() const>(&PublicistA::foo));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
class ProtectedB {
|
|
|
|
public:
|
|
|
|
virtual ~ProtectedB() = default;
|
2020-07-24 01:16:54 +00:00
|
|
|
ProtectedB() = default;
|
|
|
|
ProtectedB(const ProtectedB &) = delete;
|
2017-08-17 15:03:46 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual int foo() const { return value; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
int value = 42;
|
|
|
|
};
|
|
|
|
|
|
|
|
class TrampolineB : public ProtectedB {
|
|
|
|
public:
|
2020-09-15 12:56:20 +00:00
|
|
|
int foo() const override { PYBIND11_OVERRIDE(int, ProtectedB, foo, ); }
|
2017-08-17 15:03:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class PublicistB : public ProtectedB {
|
|
|
|
public:
|
2021-01-15 20:59:47 +00:00
|
|
|
// [workaround(intel)] = default does not work here
|
fix: Intel ICC C++17 compatibility (#2729)
* CI: Intel icc/icpc via oneAPI
Add testing for Intel icc/icpc via the oneAPI images.
Intel oneAPI is in a late beta stage, currently shipping
oneAPI beta09 with ICC 20.2.
CI: Skip Interpreter Tests for Intel
Cannot find how to add this, neiter the package `libc6-dev` nor
`intel-oneapi-mkl-devel` help when installed to solve this:
```
-- Looking for C++ include pthread.h
-- Looking for C++ include pthread.h - not found
CMake Error at /__t/cmake/3.18.4/x64/cmake-3.18.4-Linux-x86_64/share/cmake-3.18/Modules/FindPackageHandleStandardArgs.cmake:165 (message):
Could NOT find Threads (missing: Threads_FOUND)
Call Stack (most recent call first):
/__t/cmake/3.18.4/x64/cmake-3.18.4-Linux-x86_64/share/cmake-3.18/Modules/FindPackageHandleStandardArgs.cmake:458 (_FPHSA_FAILURE_MESSAGE)
/__t/cmake/3.18.4/x64/cmake-3.18.4-Linux-x86_64/share/cmake-3.18/Modules/FindThreads.cmake:234 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
tests/test_embed/CMakeLists.txt:17 (find_package)
```
CI: libc6-dev from GCC for ICC
CI: Run bare metal for oneAPI
CI: Ubuntu 18.04 for oneAPI
CI: Intel +Catch -Eigen
CI: CMake from Apt (ICC tests)
CI: Replace Intel Py with GCC Py
CI: Intel w/o GCC's Eigen
CI: ICC with verbose make
[Debug] Find core dump
tests: use arg{} instead of arg() for Intel
tests: adding a few more missing {}
fix: sync with @tobiasleibner's branch
fix: try ubuntu 20-04
fix: drop exit 1
docs: Apply suggestions from code review
Co-authored-by: Tobias Leibner <tobias.leibner@googlemail.com>
Workaround for ICC enable_if issues
Another workaround for ICC's enable_if issues
fix error in previous commit
Disable one test for the Intel compiler in C++17 mode
Add back one instance of py::arg().noconvert()
Add NOLINT to fix clang-tidy check
Work around for ICC internal error in PYBIND11_EXPAND_SIDE_EFFECTS in C++17 mode
CI: Intel ICC with C++17
docs: pybind11/numpy.h does not require numpy at build time. (#2720)
This is nice enough to be mentioned explicitly in the docs.
docs: Update warning about Python 3.9.0 UB, now that 3.9.1 has been released (#2719)
Adjusting `type_caster<std::reference_wrapper<T>>` to support const/non-const propagation in `cast_op`. (#2705)
* Allow type_caster of std::reference_wrapper<T> to be the same as a native reference.
Before, both std::reference_wrapper<T> and std::reference_wrapper<const T> would
invoke cast_op<type>. This doesn't allow the type_caster<> specialization for T
to distinguish reference_wrapper types from value types.
After, the type_caster<> specialization invokes cast_op<type&>, which allows
reference_wrapper to behave in the same way as a native reference type.
* Add tests/examples for std::reference_wrapper<const T>
* Add tests which use mutable/immutable variants
This test is a chimera; it blends the pybind11 casters with a custom
pytype implementation that supports immutable and mutable calls.
In order to detect the immutable/mutable state, the cast_op needs
to propagate it, even through e.g. std::reference<const T>
Note: This is still a work in progress; some things are crashing,
which likely means that I have a refcounting bug or something else
missing.
* Add/finish tests that distinguish const& from &
Fixes the bugs in my custom python type implementation,
demonstrate test that requires const& and reference_wrapper<const T>
being treated differently from Non-const.
* Add passing a const to non-const method.
* Demonstrate non-const conversion of reference_wrapper in tests.
Apply formatting presubmit check.
* Fix build errors from presubmit checks.
* Try and fix a few more CI errors
* More CI fixes.
* More CI fixups.
* Try and get PyPy to work.
* Additional minor fixups. Getting close to CI green.
* More ci fixes?
* fix clang-tidy warnings from presubmit
* fix more clang-tidy warnings
* minor comment and consistency cleanups
* PyDECREF -> Py_DECREF
* copy/move constructors
* Resolve codereview comments
* more review comment fixes
* review comments: remove spurious &
* Make the test fail even when the static_assert is commented out.
This expands the test_freezable_type_caster a bit by:
1/ adding accessors .is_immutable and .addr to compare identity
from python.
2/ Changing the default cast_op of the type_caster<> specialization
to return a non-const value. In normal codepaths this is a reasonable
default.
3/ adding roundtrip variants to exercise the by reference, by pointer
and by reference_wrapper in all call paths. In conjunction with 2/, this
demonstrates the failure case of the existing std::reference_wrpper conversion,
which now loses const in a similar way that happens when using the default cast_op_type<>.
* apply presubmit formatting
* Revert inclusion of test_freezable_type_caster
There's some concern that this test is a bit unwieldly because of the use
of the raw <Python.h> functions. Removing for now.
* Add a test that validates const references propagation.
This test verifies that cast_op may be used to correctly detect
const reference types when used with std::reference_wrapper.
* mend
* Review comments based changes.
1. std::add_lvalue_reference<type> -> type&
2. Simplify the test a little more; we're never returning the ConstRefCaster
type so the class_ definition can be removed.
* formatted files again.
* Move const_ref_caster test to builtin_casters
* Review comments: use cast_op and adjust some comments.
* Simplify ConstRefCasted test
I like this version better as it moves the assertion that matters
back into python.
ci: drop pypy2 linux, PGI 20.7, add Python 10 dev (#2724)
* ci: drop pypy2 linux, add Python 10 dev
* ci: fix mistake
* ci: commented-out PGI 20.11, drop 20.7
fix: regression with installed pybind11 overriding local one (#2716)
* fix: regression with installed pybind11 overriding discovered one
Closes #2709
* docs: wording incorrect
style: remove redundant instance->owned = true (#2723)
which was just before set to True in instance->allocate_layout()
fix: also throw in the move-constructor added by the PYBIND11_OBJECT macro, after the argument has been moved-out (if necessary) (#2701)
Make args_are_all_* ICC workarounds unconditional
Disable test_aligned on Intel ICC
Fix test_aligned on Intel ICC
Skip test_python_alreadyset_in_destructor on Intel ICC
Fix test_aligned again
ICC CI: Downgrade pytest
pytest 6 does not capture the `discard_as_unraisable` stderr and
just writes a warning with its content instead.
* refactor: simpler Intel workaround, suggested by @laramiel
* fix: try version with impl to see if it is easier to compile
* docs: update README for ICC
Co-authored-by: Axel Huebl <axel.huebl@plasma.ninja>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
2021-01-18 00:53:07 +00:00
|
|
|
// 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)
|
2021-01-15 20:59:47 +00:00
|
|
|
~PublicistB() override {}; // NOLINT(modernize-use-equals-default)
|
2017-08-17 15:03:46 +00:00
|
|
|
using ProtectedB::foo;
|
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<ProtectedB, TrampolineB>(m, "ProtectedB")
|
|
|
|
.def(py::init<>())
|
|
|
|
#if !defined(_MSC_VER) || _MSC_VER >= 1910
|
|
|
|
.def("foo", &PublicistB::foo);
|
|
|
|
#else
|
|
|
|
.def("foo", static_cast<int (ProtectedB::*)() const>(&PublicistB::foo));
|
|
|
|
#endif
|
2017-08-21 20:48:28 +00:00
|
|
|
|
|
|
|
// test_brace_initialization
|
|
|
|
struct BraceInitialization {
|
|
|
|
int field1;
|
|
|
|
std::string field2;
|
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<BraceInitialization>(m, "BraceInitialization")
|
|
|
|
.def(py::init<int, const std::string &>())
|
|
|
|
.def_readwrite("field1", &BraceInitialization::field1)
|
|
|
|
.def_readwrite("field2", &BraceInitialization::field2);
|
2018-01-11 17:22:13 +00:00
|
|
|
// We *don't* want to construct using braces when the given constructor argument maps to a
|
|
|
|
// constructor, because brace initialization could go to the wrong place (in particular when
|
|
|
|
// there is also an `initializer_list<T>`-accept constructor):
|
|
|
|
py::class_<NoBraceInitialization>(m, "NoBraceInitialization")
|
|
|
|
.def(py::init<std::vector<int>>())
|
|
|
|
.def_readonly("vec", &NoBraceInitialization::vec);
|
2017-08-28 14:34:06 +00:00
|
|
|
|
|
|
|
// test_reentrant_implicit_conversion_failure
|
|
|
|
// #1035: issue with runaway reentrant implicit conversion
|
|
|
|
struct BogusImplicitConversion {
|
2020-09-11 03:15:22 +00:00
|
|
|
BogusImplicitConversion(const BogusImplicitConversion &) = default;
|
2017-08-28 14:34:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<BogusImplicitConversion>(m, "BogusImplicitConversion")
|
|
|
|
.def(py::init<const BogusImplicitConversion &>());
|
|
|
|
|
|
|
|
py::implicitly_convertible<int, BogusImplicitConversion>();
|
2017-11-07 16:33:05 +00:00
|
|
|
|
|
|
|
// test_qualname
|
|
|
|
// #1166: nested class docstring doesn't show nested name
|
|
|
|
// Also related: tests that __qualname__ is set properly
|
|
|
|
struct NestBase {};
|
|
|
|
struct Nested {};
|
|
|
|
py::class_<NestBase> base(m, "NestBase");
|
|
|
|
base.def(py::init<>());
|
|
|
|
py::class_<Nested>(base, "Nested")
|
|
|
|
.def(py::init<>())
|
|
|
|
.def("fn", [](Nested &, int, NestBase &, Nested &) {})
|
|
|
|
.def("fa", [](Nested &, int, NestBase &, Nested &) {},
|
|
|
|
"a"_a, "b"_a, "c"_a);
|
|
|
|
base.def("g", [](NestBase &, Nested &) {});
|
|
|
|
base.def("h", []() { return NestBase(); });
|
2018-09-25 21:55:18 +00:00
|
|
|
|
|
|
|
// test_error_after_conversion
|
|
|
|
// The second-pass path through dispatcher() previously didn't
|
|
|
|
// remember which overload was used, and would crash trying to
|
|
|
|
// generate a useful error message
|
|
|
|
|
|
|
|
struct NotRegistered {};
|
|
|
|
struct StringWrapper { std::string str; };
|
|
|
|
m.def("test_error_after_conversions", [](int) {});
|
|
|
|
m.def("test_error_after_conversions",
|
|
|
|
[](StringWrapper) -> NotRegistered { return {}; });
|
|
|
|
py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
|
|
|
|
py::implicitly_convertible<std::string, StringWrapper>();
|
2018-11-09 19:14:53 +00:00
|
|
|
|
|
|
|
#if defined(PYBIND11_CPP17)
|
|
|
|
struct alignas(1024) Aligned {
|
|
|
|
std::uintptr_t ptr() const { return (uintptr_t) this; }
|
|
|
|
};
|
|
|
|
py::class_<Aligned>(m, "Aligned")
|
|
|
|
.def(py::init<>())
|
|
|
|
.def("ptr", &Aligned::ptr);
|
|
|
|
#endif
|
2020-04-05 06:34:00 +00:00
|
|
|
|
|
|
|
// test_final
|
|
|
|
struct IsFinal final {};
|
|
|
|
py::class_<IsFinal>(m, "IsFinal", py::is_final());
|
|
|
|
|
|
|
|
// test_non_final_final
|
|
|
|
struct IsNonFinalFinal {};
|
|
|
|
py::class_<IsNonFinalFinal>(m, "IsNonFinalFinal", py::is_final());
|
2020-08-01 00:46:12 +00:00
|
|
|
|
2020-10-08 23:09:56 +00:00
|
|
|
// test_exception_rvalue_abort
|
2020-08-01 00:46:12 +00:00
|
|
|
struct PyPrintDestructor {
|
2020-09-11 03:15:22 +00:00
|
|
|
PyPrintDestructor() = default;
|
2020-08-01 00:46:12 +00:00
|
|
|
~PyPrintDestructor() {
|
|
|
|
py::print("Print from destructor");
|
|
|
|
}
|
|
|
|
void throw_something() { throw std::runtime_error("error"); }
|
|
|
|
};
|
|
|
|
py::class_<PyPrintDestructor>(m, "PyPrintDestructor")
|
|
|
|
.def(py::init<>())
|
|
|
|
.def("throw_something", &PyPrintDestructor::throw_something);
|
2020-10-02 17:06:04 +00:00
|
|
|
|
2020-10-08 23:09:56 +00:00
|
|
|
// test_multiple_instances_with_same_pointer
|
2020-10-02 17:06:04 +00:00
|
|
|
struct SamePointer {};
|
|
|
|
static SamePointer samePointer;
|
|
|
|
py::class_<SamePointer, std::unique_ptr<SamePointer, py::nodelete>>(m, "SamePointer")
|
2021-02-01 13:52:20 +00:00
|
|
|
.def(py::init([]() { return &samePointer; }));
|
2020-10-02 17:06:04 +00:00
|
|
|
|
|
|
|
struct Empty {};
|
|
|
|
py::class_<Empty>(m, "Empty")
|
|
|
|
.def(py::init<>());
|
2020-10-08 23:09:56 +00:00
|
|
|
|
|
|
|
// test_base_and_derived_nested_scope
|
|
|
|
struct BaseWithNested {
|
|
|
|
struct Nested {};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DerivedWithNested : BaseWithNested {
|
|
|
|
struct Nested {};
|
|
|
|
};
|
|
|
|
|
|
|
|
py::class_<BaseWithNested> baseWithNested_class(m, "BaseWithNested");
|
|
|
|
py::class_<DerivedWithNested, BaseWithNested> derivedWithNested_class(m, "DerivedWithNested");
|
|
|
|
py::class_<BaseWithNested::Nested>(baseWithNested_class, "Nested")
|
|
|
|
.def_static("get_name", []() { return "BaseWithNested::Nested"; });
|
|
|
|
py::class_<DerivedWithNested::Nested>(derivedWithNested_class, "Nested")
|
|
|
|
.def_static("get_name", []() { return "DerivedWithNested::Nested"; });
|
|
|
|
|
|
|
|
// test_register_duplicate_class
|
|
|
|
struct Duplicate {};
|
|
|
|
struct OtherDuplicate {};
|
|
|
|
struct DuplicateNested {};
|
|
|
|
struct OtherDuplicateNested {};
|
|
|
|
m.def("register_duplicate_class_name", [](py::module_ m) {
|
|
|
|
py::class_<Duplicate>(m, "Duplicate");
|
|
|
|
py::class_<OtherDuplicate>(m, "Duplicate");
|
|
|
|
});
|
|
|
|
m.def("register_duplicate_class_type", [](py::module_ m) {
|
|
|
|
py::class_<OtherDuplicate>(m, "OtherDuplicate");
|
|
|
|
py::class_<OtherDuplicate>(m, "YetAnotherDuplicate");
|
|
|
|
});
|
|
|
|
m.def("register_duplicate_nested_class_name", [](py::object gt) {
|
|
|
|
py::class_<DuplicateNested>(gt, "DuplicateNested");
|
|
|
|
py::class_<OtherDuplicateNested>(gt, "DuplicateNested");
|
|
|
|
});
|
|
|
|
m.def("register_duplicate_nested_class_type", [](py::object gt) {
|
|
|
|
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
|
|
|
|
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
|
|
|
|
});
|
2017-06-08 22:44:49 +00:00
|
|
|
}
|
Allow arbitrary class_ template option ordering
The current pybind11::class_<Type, Holder, Trampoline> fixed template
ordering results in a requirement to repeat the Holder with its default
value (std::unique_ptr<Type>) argument, which is a little bit annoying:
it needs to be specified not because we want to override the default,
but rather because we need to specify the third argument.
This commit removes this limitation by making the class_ template take
the type name plus a parameter pack of options. It then extracts the
first valid holder type and the first subclass type for holder_type and
trampoline type_alias, respectively. (If unfound, both fall back to
their current defaults, `std::unique_ptr<type>` and `type`,
respectively). If any unmatched template arguments are provided, a
static assertion fails.
What this means is that you can specify or omit the arguments in any
order:
py::class_<A, PyA> c1(m, "A");
py::class_<B, PyB, std::shared_ptr<B>> c2(m, "B");
py::class_<C, std::shared_ptr<C>, PyB> c3(m, "C");
It also allows future class attributes (such as base types in the next
commit) to be passed as class template types rather than needing to use
a py::base<> wrapper.
2016-09-06 16:17:06 +00:00
|
|
|
|
2020-07-24 01:16:54 +00:00
|
|
|
template <int N> class BreaksBase { public:
|
|
|
|
virtual ~BreaksBase() = default;
|
|
|
|
BreaksBase() = default;
|
|
|
|
BreaksBase(const BreaksBase&) = delete;
|
|
|
|
};
|
Allow arbitrary class_ template option ordering
The current pybind11::class_<Type, Holder, Trampoline> fixed template
ordering results in a requirement to repeat the Holder with its default
value (std::unique_ptr<Type>) argument, which is a little bit annoying:
it needs to be specified not because we want to override the default,
but rather because we need to specify the third argument.
This commit removes this limitation by making the class_ template take
the type name plus a parameter pack of options. It then extracts the
first valid holder type and the first subclass type for holder_type and
trampoline type_alias, respectively. (If unfound, both fall back to
their current defaults, `std::unique_ptr<type>` and `type`,
respectively). If any unmatched template arguments are provided, a
static assertion fails.
What this means is that you can specify or omit the arguments in any
order:
py::class_<A, PyA> c1(m, "A");
py::class_<B, PyB, std::shared_ptr<B>> c2(m, "B");
py::class_<C, std::shared_ptr<C>, PyB> c3(m, "C");
It also allows future class attributes (such as base types in the next
commit) to be passed as class template types rather than needing to use
a py::base<> wrapper.
2016-09-06 16:17:06 +00:00
|
|
|
template <int N> class BreaksTramp : public BreaksBase<N> {};
|
|
|
|
// These should all compile just fine:
|
2020-09-11 02:57:10 +00:00
|
|
|
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 DoesntBreak3 = py::class_<BreaksBase<3>, std::unique_ptr<BreaksBase<3>>>;
|
|
|
|
using DoesntBreak4 = py::class_<BreaksBase<4>, BreaksTramp<4>>;
|
|
|
|
using DoesntBreak5 = py::class_<BreaksBase<5>>;
|
|
|
|
using DoesntBreak6 = py::class_<BreaksBase<6>, std::shared_ptr<BreaksBase<6>>, BreaksTramp<6>>;
|
|
|
|
using DoesntBreak7 = py::class_<BreaksBase<7>, BreaksTramp<7>, std::shared_ptr<BreaksBase<7>>>;
|
|
|
|
using DoesntBreak8 = py::class_<BreaksBase<8>, std::shared_ptr<BreaksBase<8>>>;
|
Allow arbitrary class_ template option ordering
The current pybind11::class_<Type, Holder, Trampoline> fixed template
ordering results in a requirement to repeat the Holder with its default
value (std::unique_ptr<Type>) argument, which is a little bit annoying:
it needs to be specified not because we want to override the default,
but rather because we need to specify the third argument.
This commit removes this limitation by making the class_ template take
the type name plus a parameter pack of options. It then extracts the
first valid holder type and the first subclass type for holder_type and
trampoline type_alias, respectively. (If unfound, both fall back to
their current defaults, `std::unique_ptr<type>` and `type`,
respectively). If any unmatched template arguments are provided, a
static assertion fails.
What this means is that you can specify or omit the arguments in any
order:
py::class_<A, PyA> c1(m, "A");
py::class_<B, PyB, std::shared_ptr<B>> c2(m, "B");
py::class_<C, std::shared_ptr<C>, PyB> c3(m, "C");
It also allows future class attributes (such as base types in the next
commit) to be passed as class template types rather than needing to use
a py::base<> wrapper.
2016-09-06 16:17:06 +00:00
|
|
|
#define CHECK_BASE(N) static_assert(std::is_same<typename DoesntBreak##N::type, BreaksBase<N>>::value, \
|
|
|
|
"DoesntBreak" #N " has wrong type!")
|
|
|
|
CHECK_BASE(1); CHECK_BASE(2); CHECK_BASE(3); CHECK_BASE(4); CHECK_BASE(5); CHECK_BASE(6); CHECK_BASE(7); CHECK_BASE(8);
|
|
|
|
#define CHECK_ALIAS(N) static_assert(DoesntBreak##N::has_alias && std::is_same<typename DoesntBreak##N::type_alias, BreaksTramp<N>>::value, \
|
|
|
|
"DoesntBreak" #N " has wrong type_alias!")
|
|
|
|
#define CHECK_NOALIAS(N) static_assert(!DoesntBreak##N::has_alias && std::is_void<typename DoesntBreak##N::type_alias>::value, \
|
|
|
|
"DoesntBreak" #N " has type alias, but shouldn't!")
|
|
|
|
CHECK_ALIAS(1); CHECK_ALIAS(2); CHECK_NOALIAS(3); CHECK_ALIAS(4); CHECK_NOALIAS(5); CHECK_ALIAS(6); CHECK_ALIAS(7); CHECK_NOALIAS(8);
|
|
|
|
#define CHECK_HOLDER(N, TYPE) static_assert(std::is_same<typename DoesntBreak##N::holder_type, std::TYPE##_ptr<BreaksBase<N>>>::value, \
|
|
|
|
"DoesntBreak" #N " has wrong holder_type!")
|
|
|
|
CHECK_HOLDER(1, unique); CHECK_HOLDER(2, unique); CHECK_HOLDER(3, unique); CHECK_HOLDER(4, unique); CHECK_HOLDER(5, unique);
|
|
|
|
CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
|
|
|
|
|
|
|
|
// There's no nice way to test that these fail because they fail to compile; leave them here,
|
|
|
|
// though, so that they can be manually tested by uncommenting them (and seeing that compilation
|
|
|
|
// failures occurs).
|
|
|
|
|
|
|
|
// We have to actually look into the type: the typedef alone isn't enough to instantiate the type:
|
|
|
|
#define CHECK_BROKEN(N) static_assert(std::is_same<typename Breaks##N::type, BreaksBase<-N>>::value, \
|
|
|
|
"Breaks1 has wrong type!");
|
|
|
|
|
|
|
|
//// Two holder classes:
|
|
|
|
//typedef py::class_<BreaksBase<-1>, std::unique_ptr<BreaksBase<-1>>, std::unique_ptr<BreaksBase<-1>>> Breaks1;
|
|
|
|
//CHECK_BROKEN(1);
|
|
|
|
//// Two aliases:
|
|
|
|
//typedef py::class_<BreaksBase<-2>, BreaksTramp<-2>, BreaksTramp<-2>> Breaks2;
|
|
|
|
//CHECK_BROKEN(2);
|
|
|
|
//// Holder + 2 aliases
|
|
|
|
//typedef py::class_<BreaksBase<-3>, std::unique_ptr<BreaksBase<-3>>, BreaksTramp<-3>, BreaksTramp<-3>> Breaks3;
|
|
|
|
//CHECK_BROKEN(3);
|
|
|
|
//// Alias + 2 holders
|
|
|
|
//typedef py::class_<BreaksBase<-4>, std::unique_ptr<BreaksBase<-4>>, BreaksTramp<-4>, std::shared_ptr<BreaksBase<-4>>> Breaks4;
|
|
|
|
//CHECK_BROKEN(4);
|
|
|
|
//// Invalid option (not a subclass or holder)
|
|
|
|
//typedef py::class_<BreaksBase<-5>, BreaksTramp<-4>> Breaks5;
|
|
|
|
//CHECK_BROKEN(5);
|
|
|
|
//// Invalid option: multiple inheritance not supported:
|
|
|
|
//template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
|
|
|
|
//typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
|
|
|
|
//CHECK_BROKEN(8);
|