diff --git a/docs/advanced.rst b/docs/advanced.rst index 1ef7fdb67..59b901ca8 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -283,8 +283,9 @@ The binding code also needs a few minor adaptations (highlighted): PYBIND11_PLUGIN(example) { py::module m("example", "pybind11 example plugin"); - py::class_, PyAnimal /* <--- trampoline*/> animal(m, "Animal"); + py::class_ animal(m, "Animal"); animal + .alias() .def(py::init<>()) .def("go", &Animal::go); @@ -296,10 +297,10 @@ The binding code also needs a few minor adaptations (highlighted): return m.ptr(); } -Importantly, pybind11 is made aware of the trampoline trampoline helper class -by specifying it as the *third* template argument to :class:`class_`. The -second argument with the unique pointer is simply the default holder type used -by pybind11. Following this, we are able to define a constructor as usual. +Importantly, the trampoline helper class is used as the template argument to +:class:`class_`, and a call to :func:`class_::alias` informs the binding +generator that this is merely an alias for the underlying type ``Animal``. +Following this, we are able to define a constructor as usual. The Python session below shows how to override ``Animal::go`` and invoke it via a virtual method call. @@ -376,8 +377,9 @@ be realized as follows (important changes highlighted): PYBIND11_PLUGIN(example) { py::module m("example", "pybind11 example plugin"); - py::class_, PyAnimal> animal(m, "Animal"); + py::class_ animal(m, "Animal"); animal + .alias() .def(py::init<>()) .def("go", &Animal::go); diff --git a/example/example12.cpp b/example/example12.cpp index e5555f53a..5cc8dc87c 100644 --- a/example/example12.cpp +++ b/example/example12.cpp @@ -82,11 +82,15 @@ void runExample12Virtual(Example12 *ex) { } void init_ex12(py::module &m) { - /* Important: indicate the trampoline class PyExample12 using the third - argument to py::class_. The second argument with the unique pointer - is simply the default holder type used by pybind11. */ - py::class_, PyExample12>(m, "Example12") + /* Important: use the wrapper type as a template + argument to class_<>, but use the original name + to denote the type */ + py::class_(m, "Example12") + /* Declare that 'PyExample12' is really an alias for the original type 'Example12' */ + .alias() .def(py::init()) + /* Copy constructor (not needed in this case, but should generally be declared in this way) */ + .def(py::init()) /* Reference original class in function definitions */ .def("run", &Example12::run) .def("run_bool", &Example12::run_bool) diff --git a/example/issues.cpp b/example/issues.cpp index d75da1cef..d1a1941b7 100644 --- a/example/issues.cpp +++ b/example/issues.cpp @@ -42,7 +42,8 @@ void init_issues(py::module &m) { } }; - py::class_, DispatchIssue>(m2, "DispatchIssue") + py::class_ base(m2, "DispatchIssue"); + base.alias() .def(py::init<>()) .def("dispatch", &Base::dispatch); @@ -107,35 +108,4 @@ void init_issues(py::module &m) { // (no id): don't cast doubles to ints m2.def("expect_float", [](float f) { return f; }); m2.def("expect_int", [](int i) { return i; }); - - // (no id): don't invoke Python dispatch code when instantiating C++ - // classes that were not extended on the Python side - struct A { - virtual ~A() {} - virtual void f() { std::cout << "A.f()" << std::endl; } - }; - - struct PyA : A { - PyA() { std::cout << "PyA.PyA()" << std::endl; } - - void f() override { - std::cout << "PyA.f()" << std::endl; - PYBIND11_OVERLOAD(void, A, f); - } - }; - - auto call_f = [](A *a) { a->f(); }; - - pybind11::class_, PyA>(m2, "A") - .def(py::init<>()) - .def("f", &A::f); - - m2.def("call_f", call_f); - - try { - py::class_(m2, "Placeholder"); - throw std::logic_error("Expected an exception!"); - } catch (std::runtime_error &) { - /* All good */ - } } diff --git a/example/issues.py b/example/issues.py index 257f08e18..d075e8340 100644 --- a/example/issues.py +++ b/example/issues.py @@ -9,7 +9,6 @@ from example.issues import Placeholder, return_vec_of_reference_wrapper from example.issues import iterator_passthrough from example.issues import ElementList, ElementA, print_element from example.issues import expect_float, expect_int -from example.issues import A, call_f import gc print_cchar("const char *") @@ -56,19 +55,3 @@ except Exception as e: print("Failed as expected: " + str(e)) print(expect_float(12)) - -class B(A): - def __init__(self): - super(B, self).__init__() - - def f(self): - print("In python f()") - -print("C++ version") -a = A() -call_f(a) - -print("Python version") -b = B() -call_f(b) - diff --git a/example/issues.ref b/example/issues.ref index 58cc7985f..4888ea55b 100644 --- a/example/issues.ref +++ b/example/issues.ref @@ -12,9 +12,3 @@ Failed as expected: Incompatible function arguments. The following argument type 1. (int) -> int Invoked with: 5.2 12.0 -C++ version -A.f() -Python version -PyA.PyA() -PyA.f() -In python f() diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 10d848ae9..4536f8b6c 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -71,7 +71,6 @@ enum op_type : int; struct undefined_t; template struct op_; template struct init; -template struct init_alias; inline void keep_alive_impl(int Nurse, int Patient, handle args, handle ret); /// Internal data structure which holds metadata about a keyword argument diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 1438e3494..af23a2d1a 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -500,7 +500,7 @@ public: NAMESPACE_BEGIN(detail) /// Generic support for creating new Python heap types class generic_type : public object { - template friend class class_; + template friend class class_; public: PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) protected: @@ -725,7 +725,7 @@ protected: }; NAMESPACE_END(detail) -template , typename type_alias = type> +template > class class_ : public detail::generic_type { public: typedef detail::instance instance_type; @@ -747,11 +747,6 @@ public: detail::process_attributes::init(extra..., &record); detail::generic_type::initialize(&record); - - if (!std::is_same::value) { - auto &instances = pybind11::detail::get_internals().registered_types_cpp; - instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; - } } template @@ -789,12 +784,6 @@ public: return *this; } - template - class_ &def(const detail::init_alias &init, const Extra&... extra) { - init.template execute(*this, extra...); - return *this; - } - template class_& def_buffer(Func &&func) { struct capture { Func func; }; capture *ptr = new capture { std::forward(func) }; @@ -882,6 +871,11 @@ public: return *this; } + template class_ alias() { + auto &instances = pybind11::detail::get_internals().registered_types_cpp; + instances[std::type_index(typeid(target))] = instances[std::type_index(typeid(type))]; + return *this; + } private: /// Initialize holder object, variant 1: object derives from enable_shared_from_this template @@ -980,31 +974,9 @@ private: NAMESPACE_BEGIN(detail) template struct init { - template ::value, int>::type = 0> - void execute(pybind11::class_ &class_, const Extra&... extra) const { + template void execute(pybind11::class_ &class_, const Extra&... extra) const { /// Function which calls a specific C++ in-place constructor - class_.def("__init__", [](Base *self_, Args... args) { new (self_) Base(args...); }, extra...); - } - - template ::value && - std::is_constructible::value, int>::type = 0> - void execute(pybind11::class_ &class_, const Extra&... extra) const { - handle cl_type = class_; - class_.def("__init__", [cl_type](handle self_, Args... args) { - if (self_.get_type() == cl_type) - new (self_.cast()) Base(args...); - else - new (self_.cast()) Alias(args...); - }, extra...); - } - - template ::value && - !std::is_constructible::value, int>::type = 0> - void execute(pybind11::class_ &class_, const Extra&... extra) const { - class_.def("__init__", [](Alias *self, Args... args) { new (self) Alias(args...); }, extra...); + class_.def("__init__", [](Base *instance, Args... args) { new (instance) Base(args...); }, extra...); } };