Skip to content

Commit

Permalink
Add initial support for multiple inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
BorisSchaeling committed Jun 12, 2016
1 parent 19d95ef commit 8158622
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 37 deletions.
1 change: 1 addition & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ set(PYBIND11_EXAMPLES
example15.cpp
example16.cpp
example17.cpp
example18.cpp
issues.cpp
)

Expand Down
2 changes: 2 additions & 0 deletions example/example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ void init_ex14(py::module &);
void init_ex15(py::module &);
void init_ex16(py::module &);
void init_ex17(py::module &);
void init_ex18(py::module &);
void init_issues(py::module &);

#if defined(PYBIND11_TEST_EIGEN)
Expand All @@ -52,6 +53,7 @@ PYBIND11_PLUGIN(example) {
init_ex15(m);
init_ex16(m);
init_ex17(m);
init_ex18(m);
init_issues(m);

#if defined(PYBIND11_TEST_EIGEN)
Expand Down
63 changes: 63 additions & 0 deletions example/example18.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
example/example18.cpp -- Multiple inheritance support
Copyright (c) 2016 Boris Schäling <boris@highscore.de>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/

#include "example.h"

struct MIParent1 {
virtual ~MIParent1() = default;

virtual void virtualParent1() {
std::cout << "virtual parent1\n";
}

void nonvirtualParent1() {
std::cout << "nonvirtual parent1\n";
}

virtual void virtualOverride() {
}
};

struct MIParent2 {
virtual ~MIParent2() = default;

virtual void virtualParent2() {
std::cout << "virtual parent2\n";
}

void nonvirtualParent2() {
std::cout << "nonvirtual parent2\n";
}
};

struct MIChild1 : MIParent1, MIParent2 {
void child1() {
std::cout << "child1\n";
}

void virtualOverride() override {
std::cout << "override\n";
}
};

void init_ex18(py::module &m) {
py::class_<MIParent1>(m, "MIParent1")
.def("virtualParent1", &MIParent1::virtualParent1)
.def("nonvirtualParent1", &MIParent1::nonvirtualParent1)
.def("virtualOverride", &MIParent1::virtualOverride);

py::class_<MIParent2>(m, "MIParent2")
.def("virtualParent2", &MIParent2::virtualParent2)
.def("nonvirtualParent2", &MIParent2::nonvirtualParent2);

py::class_<MIChild1>(m, "MIChild1", py::bases<MIParent1, MIParent2>())
.def(py::init<>())
.def("child1", &MIChild1::child1)
.def("virtualOverride", &MIChild1::virtualOverride);
}
10 changes: 10 additions & 0 deletions example/example18.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python
from example import MIChild1

c = MIChild1()
c.virtualParent1()
c.nonvirtualParent1()
c.virtualParent2()
c.nonvirtualParent2()
c.child1()
c.virtualOverride()
6 changes: 6 additions & 0 deletions example/example18.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
virtual parent1
nonvirtual parent1
virtual parent2
nonvirtual parent2
child1
override
25 changes: 16 additions & 9 deletions include/pybind11/attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ struct sibling { handle value; sibling(const handle &value) : value(value.ptr())
/// Annotation indicating that a class derives from another given type
template <typename T> struct base { };

/// Annotation indicating that a class derives from other given types
template <typename... T> struct bases { };

/// Keep patient alive while nurse lives
template <int Nurse, int Patient> struct keep_alive { };

Expand Down Expand Up @@ -161,10 +164,7 @@ struct type_record {
/// Function pointer to class_<..>::dealloc
void (*dealloc)(PyObject *) = nullptr;

// Pointer to RTTI type_info data structure of base class
const std::type_info *base_type = nullptr;

/// OR: Python handle to base class
/// Python handle to base class(es)
handle base_handle;

/// Optional docstring
Expand All @@ -182,7 +182,7 @@ template <typename T, typename SFINAE = void> struct process_attribute;
template <typename T> struct process_attribute_default {
/// Default implementation: do nothing
static void init(const T &, function_record *) { }
static void init(const T &, type_record *) { }
template <class type> static void init(const T &, type_record *) { }
static void precall(handle) { }
static void postcall(handle, handle) { }
};
Expand All @@ -200,7 +200,7 @@ template <> struct process_attribute<doc> : process_attribute_default<doc> {
/// 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 *> {
static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); }
static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); }
template <class type> static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); }
};
template <> struct process_attribute<char *> : process_attribute<const char *> { };

Expand Down Expand Up @@ -273,13 +273,19 @@ struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> {
/// Process a parent class attribute
template <typename T>
struct process_attribute<T, typename std::enable_if<std::is_base_of<handle, T>::value>::type> : process_attribute_default<handle> {
static void init(const handle &h, type_record *r) { r->base_handle = h; }
template <class type> static void init(const handle &h, type_record *r) { r->base_handle = h; }
};

/// Process a parent class attribute
template <typename T>
struct process_attribute<base<T>> : process_attribute_default<base<T>> {
static void init(const base<T> &, type_record *r) { r->base_type = &typeid(T); }
template <class type> static void init(const base<T> &, type_record *r) { r->base_handle = detail::get_type_handle(typeid(T)); }
};

/// Process a parent classes attribute
template <typename... T>
struct process_attribute<bases<T...>> : process_attribute_default<bases<T...>> {
template <class type> static void init(const bases<T...> &, type_record *r) { r->base_handle = pybind11::make_tuple(detail::register_cast<type, T>()...).release(); }
};

/***
Expand Down Expand Up @@ -307,8 +313,9 @@ template <typename... Args> struct process_attributes {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
ignore_unused(unused);
}
template <class type>
static void init(const Args&... args, type_record *r) {
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::template init<type>(args, r), 0) ... };
ignore_unused(unused);
}
static void precall(handle fn_args) {
Expand Down
48 changes: 46 additions & 2 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#include "descr.h"
#include <array>
#include <limits>
#include <utility>
#include <functional>
#include <typeindex>
#include <iostream>

NAMESPACE_BEGIN(pybind11)
Expand Down Expand Up @@ -53,7 +56,7 @@ PYBIND11_NOINLINE inline internals &get_internals() {
return *internals_ptr;
}

PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type, bool throw_if_missing = true) {
PYBIND11_NOINLINE inline detail::type_info* get_type_info(const PyTypeObject *type, bool throw_if_missing = true) {
auto const &type_dict = get_internals().registered_types_py;
do {
auto it = type_dict.find(type);
Expand Down Expand Up @@ -118,6 +121,44 @@ inline PyThreadState *get_thread_state_unchecked() {
#endif
}

template <typename Self, typename Base> handle register_cast() {
type_info *self = get_type_info(typeid(Self));
if (!self) {
self = new detail::type_info();
get_internals().registered_types_cpp[std::type_index(typeid(Self))] = self;
}

type_info *base = get_type_info(typeid(Base));
if (!base)
pybind11_fail("base type \"" + std::string(std::type_index(typeid(Base)).name()) +
"\" is not registered!");

auto &registered_casts = get_internals().registered_casts;
bool ok = registered_casts.emplace(std::make_pair(self, base), [](void *ptr){ return (Base *)(Self *)ptr; }).second;
if (!ok)
pybind11_fail("cast from \"" + std::string(std::type_index(typeid(Self)).name()) + "\" to \"" +
std::string(std::type_index(typeid(Base)).name()) + "\" is already registered!");

return get_type_handle(typeid(Base));
}

PYBIND11_NOINLINE inline const std::function<void*(void*)> *get_cast(const detail::type_info *self, const detail::type_info *base) {
auto const &cast_dict = get_internals().registered_casts;
auto it = cast_dict.find(std::make_pair((void *)self, (void *)base));
if (it != cast_dict.end())
return &it->second;
return nullptr;
}

PYBIND11_NOINLINE inline void *cast_to_base_type(handle src, const PyTypeObject *type) {
auto self = get_type_info(Py_TYPE(src.ptr()));
auto base = get_type_info(type);
auto cast_ptr = get_cast(self, base);
if (cast_ptr)
return (*cast_ptr)(((instance<void> *) src.ptr())->value);
return ((instance<void> *) src.ptr())->value;
}

class type_caster_generic {
public:
PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info)
Expand All @@ -130,7 +171,10 @@ class type_caster_generic {
value = nullptr;
return true;
} else if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) {
value = ((instance<void> *) src.ptr())->value;
if (Py_TYPE(src.ptr()) == typeinfo->type)
value = ((instance<void> *) src.ptr())->value;
else
value = cast_to_base_type(src, typeinfo->type);
return true;
}
if (convert) {
Expand Down
9 changes: 9 additions & 0 deletions include/pybind11/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
#include <unordered_map>
#include <memory>
#include <typeindex>
#include <utility>
#include <functional>

#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
Expand Down Expand Up @@ -249,6 +251,12 @@ template <typename type, typename holder_type = std::unique_ptr<type>> struct in
};

struct overload_hash {
inline std::size_t operator()(const std::pair<void*, void*>& v) const {
size_t value = std::hash<void*>()(v.first);
value ^= std::hash<void*>()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2);
return value;
}

inline std::size_t operator()(const std::pair<const PyObject *, const char *>& v) const {
size_t value = std::hash<const void *>()(v.first);
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2);
Expand All @@ -261,6 +269,7 @@ struct internals {
std::unordered_map<std::type_index, void*> registered_types_cpp; // std::type_index -> type_info
std::unordered_map<const void *, void*> registered_types_py; // PyTypeObject* -> type_info
std::unordered_map<const void *, void*> registered_instances; // void * -> PyObject*
std::unordered_map<std::pair<void*, void*>, std::function<void*(void*)>, overload_hash> registered_casts;
std::unordered_set<std::pair<const PyObject *, const char *>, overload_hash> inactive_overload_cache;
#if defined(WITH_THREAD)
decltype(PyThread_create_key()) tstate = 0; // Usually an int but a long on Cygwin64 with Python 3.x
Expand Down
47 changes: 21 additions & 26 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,26 +505,6 @@ class generic_type : public object {
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
protected:
void initialize(type_record *rec) {
if (rec->base_type) {
if (rec->base_handle)
pybind11_fail("generic_type: specified base type multiple times!");
rec->base_handle = detail::get_type_handle(*(rec->base_type));
if (!rec->base_handle) {
std::string tname(rec->base_type->name());
detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(rec->name) +
"\" referenced unknown base type \"" + tname + "\"");
}
}

auto &internals = get_internals();
auto tindex = std::type_index(*(rec->type));

if (internals.registered_types_cpp.find(tindex) !=
internals.registered_types_cpp.end())
pybind11_fail("generic_type: type \"" + std::string(rec->name) +
"\" is already registered!");

object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
object name(PYBIND11_FROM_STRING(rec->name), false);
auto type = (PyHeapTypeObject*) type_holder.ptr();
Expand All @@ -533,12 +513,15 @@ class generic_type : public object {
pybind11_fail("generic_type: unable to create type object!");

/* Register supplemental type information in C++ dict */
detail::type_info *tinfo = new detail::type_info();
detail::type_info *tinfo = get_type_info(*(rec->type));
if (!tinfo) {
tinfo = new detail::type_info();
get_internals().registered_types_cpp[std::type_index(*(rec->type))] = tinfo;
}
tinfo->type = (PyTypeObject *) type;
tinfo->type_size = rec->type_size;
tinfo->init_holder = rec->init_holder;
internals.registered_types_cpp[tindex] = tinfo;
internals.registered_types_py[type] = tinfo;
get_internals().registered_types_py[type] = tinfo;

object scope_module;
if (rec->scope) {
Expand All @@ -552,8 +535,16 @@ class generic_type : public object {
/* Basic type attributes */
type->ht_type.tp_name = strdup(full_name.c_str());
type->ht_type.tp_basicsize = (ssize_t) rec->instance_size;
type->ht_type.tp_base = (PyTypeObject *) rec->base_handle.ptr();
rec->base_handle.inc_ref();
if (rec->base_handle) {
tuple t(object(rec->base_handle, false));
if (t.check()) {
type->ht_type.tp_base = (PyTypeObject *) ((object) t[0]).ptr();
type->ht_type.tp_bases = rec->base_handle.ptr();
} else {
type->ht_type.tp_base = (PyTypeObject *) rec->base_handle.ptr();
}
rec->base_handle.inc_ref();
}

#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
/* Qualified names for Python >= 3.3 */
Expand Down Expand Up @@ -734,6 +725,10 @@ class class_ : public detail::generic_type {

template <typename... Extra>
class_(handle scope, const char *name, const Extra &... extra) {
if (detail::get_type_info(typeid(type)))
pybind11_fail("generic_type: type \"" + std::string(name) +
"\" is already registered!");

detail::type_record record;
record.scope = scope;
record.name = name;
Expand All @@ -744,7 +739,7 @@ class class_ : public detail::generic_type {
record.dealloc = dealloc;

/* Process optional arguments, if any */
detail::process_attributes<Extra...>::init(extra..., &record);
detail::process_attributes<Extra...>::template init<type>(extra..., &record);

detail::generic_type::initialize(&record);

Expand Down

0 comments on commit 8158622

Please sign in to comment.