Skip to content

Commit d7efa4f

Browse files
committed
return best representation of polymorphic types (fixes #105)
1 parent d40885a commit d7efa4f

File tree

7 files changed

+55
-4
lines changed

7 files changed

+55
-4
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ set(PYBIND11_EXAMPLES
121121
example/example13.cpp
122122
example/example14.cpp
123123
example/example15.cpp
124+
example/example16.cpp
124125
example/issues.cpp
125126
)
126127

docs/changelog.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ Changelog
55

66
1.5 (not yet released)
77
----------------------
8-
* Pickling support
8+
* For polymorphic types, use RTTI to try to return the closest type registered with pybind11.
9+
* Pickling support for serializing and unserializing C++ instances to a byte stream in Python
910
* Added a variadic ``make_tuple()`` function
1011
* Address a rare issue that could confuse the current virtual function dispatcher
1112
* Documentation improvements: import issues, symbol visibility, pickling, limitations

example/example.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ void init_ex12(py::module &);
2424
void init_ex13(py::module &);
2525
void init_ex14(py::module &);
2626
void init_ex15(py::module &);
27+
void init_ex16(py::module &);
2728
void init_issues(py::module &);
2829

2930
PYBIND11_PLUGIN(example) {
@@ -44,6 +45,7 @@ PYBIND11_PLUGIN(example) {
4445
init_ex13(m);
4546
init_ex14(m);
4647
init_ex15(m);
48+
init_ex16(m);
4749
init_issues(m);
4850

4951
return m.ptr();

example/example16.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
example/example16.cpp -- automatic upcasting for polymorphic types
3+
4+
Copyright (c) 2015 Wenzel Jakob <[email protected]>
5+
6+
All rights reserved. Use of this source code is governed by a
7+
BSD-style license that can be found in the LICENSE file.
8+
*/
9+
10+
#include "example.h"
11+
12+
struct BaseClass { virtual ~BaseClass() {} };
13+
struct DerivedClass1 : BaseClass { };
14+
struct DerivedClass2 : BaseClass { };
15+
16+
void init_ex16(py::module &m) {
17+
py::class_<BaseClass>(m, "BaseClass").def(py::init<>());
18+
py::class_<DerivedClass1>(m, "DerivedClass1").def(py::init<>());
19+
py::class_<DerivedClass2>(m, "DerivedClass2").def(py::init<>());
20+
21+
m.def("return_class_1", []() -> BaseClass* { return new DerivedClass1(); });
22+
m.def("return_class_2", []() -> BaseClass* { return new DerivedClass2(); });
23+
m.def("return_none", []() -> BaseClass* { return nullptr; });
24+
}

example/example16.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from __future__ import print_function
2+
import sys
3+
4+
sys.path.append('.')
5+
6+
from example import return_class_1
7+
from example import return_class_2
8+
from example import return_none
9+
10+
print(type(return_class_1()))
11+
print(type(return_class_2()))
12+
print(type(return_none()))

example/example16.ref

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<class 'example.DerivedClass1'>
2+
<class 'example.DerivedClass2'>
3+
<type 'NoneType'>

include/pybind11/cast.h

+11-3
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class type_caster_generic {
137137

138138
PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent,
139139
const std::type_info *type_info,
140+
const std::type_info *type_info_backup,
140141
void *(*copy_constructor)(const void *),
141142
const void *existing_holder = nullptr) {
142143
void *src = const_cast<void *>(_src);
@@ -153,6 +154,11 @@ class type_caster_generic {
153154
return handle((PyObject *) it_instance->second).inc_ref();
154155

155156
auto it = internals.registered_types_cpp.find(std::type_index(*type_info));
157+
if (it == internals.registered_types_cpp.end()) {
158+
type_info = type_info_backup;
159+
it = internals.registered_types_cpp.find(std::type_index(*type_info));
160+
}
161+
156162
if (it == internals.registered_types_cpp.end()) {
157163
std::string tname = type_info->name();
158164
detail::clean_type_id(tname);
@@ -213,11 +219,11 @@ template <typename type, typename Enable = void> class type_caster : public type
213219
static handle cast(const type &src, return_value_policy policy, handle parent) {
214220
if (policy == return_value_policy::automatic)
215221
policy = return_value_policy::copy;
216-
return type_caster_generic::cast(&src, policy, parent, &typeid(type), &copy_constructor);
222+
return cast(&src, policy, parent);
217223
}
218224

219225
static handle cast(const type *src, return_value_policy policy, handle parent) {
220-
return type_caster_generic::cast(src, policy, parent, &typeid(type), &copy_constructor);
226+
return type_caster_generic::cast(src, policy, parent, src ? &typeid(*src) : nullptr, &typeid(type), &copy_constructor);
221227
}
222228

223229
template <typename T> using cast_op_type = pybind11::detail::cast_op_type<T>;
@@ -664,7 +670,9 @@ template <typename type, typename holder_type> class type_caster_holder : public
664670

665671
static handle cast(const holder_type &src, return_value_policy policy, handle parent) {
666672
return type_caster_generic::cast(
667-
src.get(), policy, parent, &typeid(type), &copy_constructor, &src);
673+
src.get(), policy, parent,
674+
src.get() ? &typeid(*src.get()) : nullptr, &typeid(type),
675+
&copy_constructor, &src);
668676
}
669677

670678
protected:

0 commit comments

Comments
 (0)