mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 05:05:11 +00:00
Allow specifying custom base classes
- Useful for object hierarchies that don't use C++ inheritance (such as GObject)
This commit is contained in:
parent
964c49978f
commit
d6df11602b
@ -65,6 +65,31 @@ struct base {
|
|||||||
base() = default;
|
base() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
Annotation indicating that a class should appear to python to derive from
|
||||||
|
another given type. This is useful for wrapping type systems that don't
|
||||||
|
utilize standard C++ inheritance.
|
||||||
|
|
||||||
|
You must provide a caster function that casts from the derived type to
|
||||||
|
the base type. As an example, standard C++ inheritance would do this:
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
py::class_<Derived> cls(m, "Derived", py::custom_base<Base>([](void *o) {
|
||||||
|
return static_cast<Base*>(reinterpret_cast<Derived*>(o));
|
||||||
|
}));
|
||||||
|
|
||||||
|
.. note:: This is an advanced feature. If you use this, you likely need
|
||||||
|
to implement polymorphic_type_hook for your type hierarchy.
|
||||||
|
\endrst */
|
||||||
|
template <typename T> struct custom_base {
|
||||||
|
using caster = void *(*)(void *);
|
||||||
|
|
||||||
|
explicit custom_base(const caster &f) : fn(f) {}
|
||||||
|
caster fn;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Keep patient alive while nurse lives
|
/// Keep patient alive while nurse lives
|
||||||
template <size_t Nurse, size_t Patient>
|
template <size_t Nurse, size_t Patient>
|
||||||
struct keep_alive {};
|
struct keep_alive {};
|
||||||
@ -551,6 +576,17 @@ struct process_attribute<base<T>> : process_attribute_default<base<T>> {
|
|||||||
static void init(const base<T> &, type_record *r) { r->add_base(typeid(T), nullptr); }
|
static void init(const base<T> &, type_record *r) { r->add_base(typeid(T), nullptr); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Process a custom base attribute
|
||||||
|
template <typename T>
|
||||||
|
struct process_attribute<custom_base<T>> : process_attribute_default<custom_base<T>> {
|
||||||
|
static void init(const custom_base<T> &b, type_record *r)
|
||||||
|
{
|
||||||
|
r->add_base(typeid(T), b.fn);
|
||||||
|
// TODO: rename this to 'nonsimple'?
|
||||||
|
r->multiple_inheritance = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// 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> {
|
||||||
|
@ -125,6 +125,7 @@ set(PYBIND11_TEST_FILES
|
|||||||
test_const_name
|
test_const_name
|
||||||
test_constants_and_functions
|
test_constants_and_functions
|
||||||
test_copy_move
|
test_copy_move
|
||||||
|
test_custom_base
|
||||||
test_custom_type_casters
|
test_custom_type_casters
|
||||||
test_custom_type_setup
|
test_custom_type_setup
|
||||||
test_docstring_options
|
test_docstring_options
|
||||||
|
39
tests/test_custom_base.cpp
Normal file
39
tests/test_custom_base.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
tests/test_custom_base.cpp -- test custom type hierarchy support
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct Base {
|
||||||
|
int i = 5;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Derived {
|
||||||
|
int j = 6;
|
||||||
|
|
||||||
|
// just to prove the base can be anywhere
|
||||||
|
Base base;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_SUBMODULE(custom_base, m) {
|
||||||
|
|
||||||
|
py::class_<Base>(m, "Base").def_readwrite("i", &Base::i);
|
||||||
|
|
||||||
|
py::class_<Derived>(m, "Derived", py::custom_base<Base>([](void *o) -> void * {
|
||||||
|
return &reinterpret_cast<Derived *>(o)->base;
|
||||||
|
})).def_readwrite("j", &Derived::j);
|
||||||
|
|
||||||
|
m.def("create_derived", []() { return new Derived; });
|
||||||
|
m.def("create_base", []() { return new Base; });
|
||||||
|
|
||||||
|
m.def("base_i", [](Base *b) { return b->i; });
|
||||||
|
|
||||||
|
m.def("derived_j", [](Derived *d) { return d->j; });
|
||||||
|
};
|
23
tests/test_custom_base.py
Normal file
23
tests/test_custom_base.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from pybind11_tests import custom_base as m
|
||||||
|
|
||||||
|
|
||||||
|
def test_cb_base():
|
||||||
|
b = m.create_base()
|
||||||
|
|
||||||
|
assert isinstance(b, m.Base)
|
||||||
|
assert b.i == 5
|
||||||
|
|
||||||
|
assert m.base_i(b) == 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_cb_derived():
|
||||||
|
d = m.create_derived()
|
||||||
|
|
||||||
|
assert isinstance(d, m.Derived)
|
||||||
|
assert isinstance(d, m.Base)
|
||||||
|
|
||||||
|
assert d.i == 5
|
||||||
|
assert d.j == 6
|
||||||
|
|
||||||
|
assert m.base_i(d) == 5
|
||||||
|
assert m.derived_j(d) == 6
|
Loading…
Reference in New Issue
Block a user