Skip to content

Commit 0c8aa84

Browse files
committed
Enable automatic downcasting to registered classes for pointers, references, and smart pointers
[SVN r16673]
1 parent 3d874d1 commit 0c8aa84

File tree

9 files changed

+187
-40
lines changed

9 files changed

+187
-40
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright David Abrahams 2002. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#ifndef DECREF_GUARD_DWA20021220_HPP
7+
# define DECREF_GUARD_DWA20021220_HPP
8+
9+
namespace boost { namespace python { namespace detail {
10+
11+
struct decref_guard
12+
{
13+
decref_guard(PyObject* o) : obj(o) {}
14+
~decref_guard() { Py_XDECREF(obj); }
15+
void cancel() { obj = 0; }
16+
private:
17+
PyObject* obj;
18+
};
19+
20+
}}} // namespace boost::python::detail
21+
22+
#endif // DECREF_GUARD_DWA20021220_HPP

include/boost/python/object/make_instance.hpp

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,42 +8,34 @@
88

99
# include <boost/python/object/instance.hpp>
1010
# include <boost/python/converter/registered.hpp>
11+
# include <boost/python/detail/decref_guard.hpp>
1112

1213
namespace boost { namespace python { namespace objects {
1314

14-
struct decref_guard
15-
{
16-
decref_guard(PyObject* o) : obj(o) {}
17-
~decref_guard() { Py_XDECREF(obj); }
18-
void cancel() { obj = 0; }
19-
private:
20-
PyObject* obj;
21-
};
22-
23-
template <class T, class Holder>
24-
struct make_instance
15+
template <class T, class Holder, class Derived>
16+
struct make_instance_impl
2517
{
2618
typedef objects::instance<Holder> instance_t;
2719

2820
template <class Arg>
29-
static PyObject* execute(Arg& x)
21+
static inline PyObject* execute(Arg& x)
3022
{
3123
BOOST_STATIC_ASSERT(is_class<T>::value);
32-
33-
PyTypeObject* type = converter::registered<T>::converters.get_class_object();
24+
25+
PyTypeObject* type = Derived::get_class_object(x);
3426

3527
PyObject* raw_result = type->tp_alloc(
3628
type, objects::additional_instance_size<Holder>::value);
3729

3830
if (raw_result != 0)
3931
{
40-
decref_guard protect(raw_result);
32+
python::detail::decref_guard protect(raw_result);
4133

4234
instance_t* instance = (instance_t*)raw_result;
4335

4436
// construct the new C++ object and install the pointer
4537
// in the Python object.
46-
construct(instance, x)->install(raw_result);
38+
Derived::construct(&instance->storage, (PyObject*)instance, x)->install(raw_result);
4739

4840
// Note the position of the internally-stored Holder,
4941
// for the sake of destruction
@@ -54,23 +46,22 @@ struct make_instance
5446
}
5547
return raw_result;
5648
}
49+
};
5750

58-
private:
59-
// Kind of a hack to support code re-use. The first form is used
60-
// to construct holders around pointers or smart pointers. The
61-
// second form is used to construct holders around by-value
62-
// returns. We have to pass a pointer to the owning Python object
63-
// to the second form in order to make it forward the 2nd argument
64-
// on to the constructor of its embedded T object.
65-
template <class Arg>
66-
static Holder* construct(instance_t* result, Arg& x)
51+
52+
template <class T, class Holder>
53+
struct make_instance
54+
: make_instance_impl<T, Holder, make_instance<T,Holder> >
55+
{
56+
template <class U>
57+
static inline PyTypeObject* get_class_object(U&)
6758
{
68-
return new ((void*)&result->storage) Holder(x);
59+
return converter::registered<T>::converters.get_class_object();
6960
}
70-
71-
static Holder* construct(instance_t* result, reference_wrapper<T const> x)
61+
62+
static inline Holder* construct(void* storage, PyObject* instance, reference_wrapper<T const> x)
7263
{
73-
return new ((void*)&result->storage) Holder((PyObject*)result, x);
64+
return new (storage) Holder(instance, x);
7465
}
7566
};
7667

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright David Abrahams 2002. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#ifndef MAKE_PTR_INSTANCE_DWA200296_HPP
7+
# define MAKE_PTR_INSTANCE_DWA200296_HPP
8+
9+
# include <boost/python/object/make_instance.hpp>
10+
# include <boost/python/converter/registry.hpp>
11+
# include <boost/type_traits/is_polymorphic.hpp>
12+
# include <boost/get_pointer.hpp>
13+
# include <typeinfo>
14+
15+
namespace boost { namespace python { namespace objects {
16+
17+
template <class T, class Holder>
18+
struct make_ptr_instance
19+
: make_instance_impl<T, Holder, make_ptr_instance<T,Holder> >
20+
{
21+
template <class Arg>
22+
static inline Holder* construct(void* storage, PyObject*, Arg& x)
23+
{
24+
return new (storage) Holder(x);
25+
}
26+
27+
template <class Ptr>
28+
static inline PyTypeObject* get_class_object(Ptr const& x)
29+
{
30+
return get_class_object_impl(get_pointer(x));
31+
}
32+
33+
private:
34+
template <class U>
35+
static inline PyTypeObject* get_class_object_impl(U const volatile* p)
36+
{
37+
PyTypeObject* derived = get_derived_class_object(is_polymorphic<U>::type(), p);
38+
if (derived)
39+
return derived;
40+
return converter::registered<T>::converters.get_class_object();
41+
}
42+
43+
template <class U>
44+
static inline PyTypeObject* get_derived_class_object(mpl::true_c, U const volatile* x)
45+
{
46+
converter::registration const* r = converter::registry::query(type_info(typeid(*x)));
47+
return r ? r->m_class_object : 0;
48+
}
49+
50+
template <class U>
51+
static inline PyTypeObject* get_derived_class_object(mpl::false_c, U*)
52+
{
53+
return 0;
54+
}
55+
};
56+
57+
58+
}}} // namespace boost::python::object
59+
60+
#endif // MAKE_PTR_INSTANCE_DWA200296_HPP

include/boost/python/object/select_holder.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# include <boost/python/object/value_holder.hpp>
1313
# include <boost/python/object/pointer_holder.hpp>
1414
# include <boost/python/object/class_wrapper.hpp>
15-
# include <boost/python/object/make_instance.hpp>
15+
# include <boost/python/object/make_ptr_instance.hpp>
1616
# include <boost/python/object/instance.hpp>
1717
# include <boost/python/detail/force_instantiate.hpp>
1818
# include <boost/type.hpp>
@@ -142,7 +142,7 @@ namespace detail
142142
static inline void register_(mpl::false_c)
143143
{
144144
python::detail::force_instantiate(
145-
objects::class_value_wrapper<Ptr, make_instance<T,type> >());
145+
objects::class_value_wrapper<Ptr, make_ptr_instance<T,type> >());
146146
}
147147
};
148148
}

include/boost/python/to_python_indirect.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# include <boost/python/detail/unwind_type.hpp>
1414
# include <boost/python/detail/none.hpp>
1515
# include <boost/shared_ptr.hpp>
16-
# include <boost/python/object/make_instance.hpp>
16+
# include <boost/python/object/make_ptr_instance.hpp>
1717
# include <memory>
1818

1919
namespace boost { namespace python {
@@ -48,7 +48,7 @@ namespace detail
4848
typedef objects::pointer_holder<smart_pointer, T> holder_t;
4949

5050
smart_pointer ptr(p);
51-
return objects::make_instance<T, holder_t>::execute(ptr);
51+
return objects::make_ptr_instance<T, holder_t>::execute(ptr);
5252
}
5353
};
5454

@@ -59,7 +59,7 @@ namespace detail
5959
static result_type execute(T* p)
6060
{
6161
typedef objects::pointer_holder<T*, T> holder_t;
62-
return objects::make_instance<T, holder_t>::execute(p);
62+
return objects::make_ptr_instance<T, holder_t>::execute(p);
6363
}
6464
};
6565

test/polymorphism.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
// copyright notice appears in all copies. This software is provided
44
// "as is" without express or implied warranty, and with no claim as
55
// to its suitability for any purpose.
6-
#include <boost/python.hpp>
6+
#include <boost/python/module.hpp>
7+
#include <boost/python/class.hpp>
8+
#include <boost/python/return_value_policy.hpp>
9+
#include <boost/python/manage_new_object.hpp>
10+
#include <boost/python/reference_existing_object.hpp>
11+
#include <boost/python/call_method.hpp>
12+
#include <boost/python/def.hpp>
13+
#include <boost/utility.hpp>
714

815
using namespace boost::python;
916

@@ -40,6 +47,11 @@ struct B : A
4047
virtual std::string f() { return "B::f()"; }
4148
};
4249

50+
struct C : A
51+
{
52+
virtual std::string f() { return "C::f()"; }
53+
};
54+
4355
A& getBCppObj ()
4456
{
4557
static B b;
@@ -48,6 +60,25 @@ A& getBCppObj ()
4860

4961
std::string call_f(A& a) { return a.f(); }
5062

63+
A* factory(unsigned choice)
64+
{
65+
switch (choice % 3)
66+
{
67+
case 0: return new A;
68+
break;
69+
case 1: return new B;
70+
break;
71+
default: return new C;
72+
break;
73+
}
74+
}
75+
76+
C& getCCppObj ()
77+
{
78+
static C c;
79+
return c;
80+
}
81+
5182
BOOST_PYTHON_MODULE_INIT(polymorphism_ext)
5283
{
5384
class_<A,boost::noncopyable,ACallback>("A")
@@ -56,6 +87,14 @@ BOOST_PYTHON_MODULE_INIT(polymorphism_ext)
5687

5788
def("getBCppObj", getBCppObj, return_value_policy<reference_existing_object>());
5889

90+
class_<C,bases<A>,boost::noncopyable>("C")
91+
.def("f", &C::f)
92+
;
93+
94+
def("getCCppObj", getCCppObj, return_value_policy<reference_existing_object>());
95+
96+
def("factory", factory, return_value_policy<manage_new_object>());
97+
5998
def("call_f", call_f);
6099
}
61100

test/polymorphism.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,31 @@ def testReturnCpp(self):
1717
self.failUnlessEqual ('B::f()', a.f())
1818
self.failUnlessEqual ('B::f()', call_f(a))
1919
self.failUnlessEqual ('A::f()', call_f(A()))
20+
21+
def test_references(self):
22+
# B is not exposed to Python
23+
a = getBCppObj()
24+
self.failUnlessEqual(type(a), A)
25+
26+
# C is exposed to Python
27+
c = getCCppObj()
28+
self.failUnlessEqual(type(c), C)
2029

30+
def test_factory(self):
31+
self.failUnlessEqual(type(factory(0)), A)
32+
self.failUnlessEqual(type(factory(1)), A)
33+
self.failUnlessEqual(type(factory(2)), C)
34+
2135
def testReturnPy(self):
2236

23-
class C(A):
37+
class D(A):
2438
def f(self):
25-
return 'C.f'
39+
return 'D.f'
2640

27-
c = C()
41+
d = D()
2842

29-
self.failUnlessEqual ('C.f', c.f())
30-
self.failUnlessEqual ('C.f', call_f(c))
43+
self.failUnlessEqual ('D.f', d.f())
44+
self.failUnlessEqual ('D.f', call_f(d))
3145

3246
if __name__ == "__main__":
3347

test/shared_ptr.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ struct ZWrap : Z
8282
PyObject* m_self;
8383
};
8484

85+
struct YY : Y
86+
{
87+
YY(int n) : Y(n) {}
88+
};
89+
90+
shared_ptr<Y> factory(int n)
91+
{
92+
return shared_ptr<Y>(n < 42 ? new Y(n) : new YY(n));
93+
}
94+
8595
static int stored_v() { return functions<Z>::get()->v(); }
8696

8797
BOOST_PYTHON_MODULE(shared_ptr_ext)
@@ -90,6 +100,8 @@ BOOST_PYTHON_MODULE(shared_ptr_ext)
90100
.def("value", &X::value)
91101
;
92102

103+
def("factory", factory);
104+
93105
functions<X>::expose();
94106
def("x_count", &X::count);
95107
def("x_release", &functions<X>::release_store);
@@ -99,6 +111,9 @@ BOOST_PYTHON_MODULE(shared_ptr_ext)
99111
.def("value", &Y::value)
100112
;
101113

114+
class_<YY, bases<Y>, boost::noncopyable>("YY", init<int>())
115+
;
116+
102117
functions<Y>::expose();
103118
def("y_count", &Y::count);
104119
def("y_release", &functions<Y>::release_store);

test/shared_ptr.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
'''
22
>>> from shared_ptr_ext import *
3+
4+
>>> type(factory(3))
5+
<class 'shared_ptr_ext.Y'>
6+
>>> type(factory(42))
7+
<class 'shared_ptr_ext.YY'>
8+
39
>>> class P(Z):
410
... def v(self):
511
... return -Z.v(self);

0 commit comments

Comments
 (0)