Skip to content

Commit d27e5a5

Browse files
committed
Rationalize object_manager
[SVN r14548]
1 parent 7ecf764 commit d27e5a5

16 files changed

Lines changed: 370 additions & 206 deletions

include/boost/python/converter/arg_to_python.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
# include <boost/python/base_type_traits.hpp>
1919
// Bring in specializations
2020
# include <boost/python/converter/builtin_converters.hpp>
21+
# include <boost/python/tag.hpp>
2122

2223
namespace boost { namespace python { namespace converter {
2324

@@ -64,7 +65,7 @@ namespace detail
6465

6566
PyObject* get() const
6667
{
67-
return python::upcast<PyObject>(converter::get_managed_object(m_src));
68+
return python::upcast<PyObject>(get_managed_object(m_src, tag));
6869
}
6970

7071
private:

include/boost/python/converter/obj_mgr_arg_from_python.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# include <boost/python/detail/construct.hpp>
1313
# include <boost/python/converter/object_manager.hpp>
1414
# include <boost/python/detail/raw_pyobject.hpp>
15+
# include <boost/python/tag.hpp>
1516

1617
//
1718
// arg_from_python converters for Python type wrappers, to be used as
@@ -31,6 +32,15 @@ struct object_manager_value_arg_from_python
3132
PyObject* m_source;
3233
};
3334

35+
// Used for converting reference-to-object-manager arguments from
36+
// python. The process used here is a little bit odd. Upon
37+
// construction, we build the object manager object in the m_result
38+
// object, *forcing* it to accept the source Python object by casting
39+
// its pointer to detail::borrowed_reference. This is supposed to
40+
// bypass any type checking of the source object. The convertible
41+
// check then extracts the owned object and checks it. If the check
42+
// fails, nothing else in the program ever gets to touch this strange
43+
// "forced" object.
3444
template <class Ref>
3545
struct object_manager_ref_arg_from_python
3646
{
@@ -83,7 +93,7 @@ namespace detail
8393
template <class T>
8494
inline bool object_manager_ref_check(T const& x)
8595
{
86-
return object_manager_traits<T>::check((get_managed_object)(x));
96+
return object_manager_traits<T>::check(get_managed_object(x, tag));
8797
}
8898
}
8999

include/boost/python/converter/object_manager.hpp

Lines changed: 130 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88

99
# include <boost/python/handle.hpp>
1010
# include <boost/python/cast.hpp>
11+
# include <boost/python/converter/pyobject_traits.hpp>
1112
# include <boost/type_traits/object_traits.hpp>
1213
# include <boost/mpl/select_type.hpp>
1314
# include <boost/python/detail/indirect_traits.hpp>
1415

1516
// Facilities for dealing with types which always manage Python
16-
// objects. Some examples are object, list, et. al. Different
17+
// objects. Some examples are object, list, str, et. al. Different
1718
// to_python/from_python conversion rules apply here because in
1819
// contrast to other types which are typically embedded inside a
1920
// Python object, these are wrapped around a Python object. For most
@@ -22,47 +23,103 @@
2223
// Python argument, since mutating member functions on T actually only
2324
// modify the held Python object.
2425
//
25-
// Note also that handle<> does not qualify as an object manager because:
26-
// a. It might not manage a Python object (it can be null)
27-
// b. Mutating operations visible to users modify the handle<> itself.
28-
29-
namespace boost { namespace python { namespace api
30-
{
31-
class object; // forward declaration
32-
}}}
26+
// handle<T> is an object manager, though strictly speaking it should
27+
// not be. In other words, even though mutating member functions of
28+
// hanlde<T> actually modify the handle<T> and not the T object,
29+
// handle<T>& arguments of wrapped functions will bind to "rvalues"
30+
// wrapping the actual Python argument, just as with other object
31+
// manager classes. Making an exception for handle<T> is simply not
32+
// worth the trouble.
33+
//
34+
// borrowed<T> cv* is an object manager so that we can use the general
35+
// to_python mechanisms to convert raw Python object pointers to
36+
// python, without the usual semantic problems of using raw pointers.
3337

34-
namespace boost { namespace python { namespace converter {
3538

36-
// Used to create object managers of type T, taking ownership of a
37-
// given PyObject*. Specializations X must satisfy the following,
38-
// where p is a non-null PyObject*:
39+
// Object Manager Concept requirements:
40+
//
41+
// T is an Object Manager
42+
// p is a PyObject*
43+
// x is a T
44+
//
45+
// * object_manager_traits<T>::is_specialized == true
46+
//
47+
// * T(detail::borrowed_reference(p))
48+
// Manages p without checking its type
49+
//
50+
// * get_managed_object(x, boost::python::tag)
51+
// Convertible to PyObject*
3952
//
40-
// X::is_specialized == true
53+
// Additional requirements if T can be converted from_python:
4154
//
42-
// T(X::adopt(p)) - constructs a T object, stealing a reference to
43-
// p, or throws a TypeError exception if p doesn't have an
44-
// appropriate type.
55+
// * T(object_manager_traits<T>::adopt(p))
56+
// steals a reference to p, or throws a TypeError exception if
57+
// p doesn't have an appropriate type. May assume p is non-null
4558
//
46-
// X::check(p), convertible to bool. True iff T(X::construct(p)) will
47-
// not throw.
59+
// * X::check(p)
60+
// convertible to bool. True iff T(X::construct(p)) will not
61+
// throw.
62+
63+
// Forward declarations
4864
//
65+
namespace boost { namespace python
66+
{
67+
namespace api
68+
{
69+
class object;
70+
}
71+
}}
72+
73+
namespace boost { namespace python { namespace converter {
74+
75+
76+
// Specializations for handle<T>
77+
template <class T>
78+
struct handle_object_manager_traits
79+
: pyobject_traits<typename T::element_type>
80+
{
81+
private:
82+
typedef pyobject_traits<typename T::element_type> base;
83+
84+
public:
85+
BOOST_STATIC_CONSTANT(bool, is_specialized = true);
86+
87+
// Initialize with a null_ok pointer for efficiency, bypassing the
88+
// null check since the source is always non-null.
89+
static null_ok<typename T::element_type>* adopt(PyObject* p)
90+
{
91+
return python::allow_null(base::checked_downcast(p));
92+
}
93+
};
94+
95+
template <class T>
96+
struct default_object_manager_traits
97+
{
98+
BOOST_STATIC_CONSTANT(
99+
bool, is_specialized = python::detail::is_borrowed_ptr<T>::value
100+
);
101+
};
102+
49103
template <class T>
50104
struct object_manager_traits
105+
: mpl::select_type<
106+
is_handle<T>::value
107+
, handle_object_manager_traits<T>
108+
, default_object_manager_traits<T>
109+
>::type
51110
{
52-
BOOST_STATIC_CONSTANT(bool, is_specialized = false);
53111
};
54112

55-
// A metafunction returning true iff its argument is an object manager.
113+
//
114+
// Traits for detecting whether a type is an object manager or a
115+
// (cv-qualified) reference to an object manager.
116+
//
117+
56118
template <class T>
57119
struct is_object_manager
58120
{
59-
private:
60-
// handle the cases that would otherwise require partial specialization
61-
BOOST_STATIC_CONSTANT(bool, hdl = is_handle<T>::value);
62-
BOOST_STATIC_CONSTANT(bool, borrowed = python::detail::is_borrowed_ptr<T>::value);
63-
BOOST_STATIC_CONSTANT(bool, traits_specialized = object_manager_traits<T>::is_specialized);
64-
public:
65-
BOOST_STATIC_CONSTANT(bool, value = (hdl | borrowed | traits_specialized));
121+
BOOST_STATIC_CONSTANT(
122+
bool, value = object_manager_traits<T>::is_specialized);
66123
};
67124

68125
# ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
@@ -102,84 +159,74 @@ namespace detail
102159
typedef char (&yes_reference_to_object_manager)[1];
103160
typedef char (&no_reference_to_object_manager)[2];
104161

162+
// A number of nastinesses go on here in order to work around MSVC6
163+
// bugs.
105164
template <class T>
106165
struct is_object_manager_help
107-
: mpl::select_type<
108-
is_object_manager<T>::value
109-
, yes_reference_to_object_manager
110-
, no_reference_to_object_manager>
111166
{
167+
typedef typename mpl::select_type<
168+
is_object_manager<T>::value
169+
, yes_reference_to_object_manager
170+
, no_reference_to_object_manager
171+
>::type type;
172+
173+
// If we just use the type instead of the result of calling this
174+
// function, VC6 will ICE.
175+
static type call();
112176
};
113177

114-
template <bool is_ref = false>
115-
struct is_reference_to_object_manager_helper
116-
{
117-
template <class T>
118-
struct apply
119-
{
120-
static int x;
121-
static no_reference_to_object_manager check(...);
122-
};
123-
};
124-
178+
// A set of overloads for each cv-qualification. The same argument
179+
// is passed twice: the first one is used to unwind the cv*, and the
180+
// second one is used to avoid relying on partial ordering for
181+
// overload resolution.
125182
template <class U>
126-
typename is_object_manager_help<U>::type
127-
is_object_manager_helper(int*, double, double, U&);
183+
typename is_object_manager_help<U>
184+
is_object_manager_helper(U*, void*);
128185

129186
template <class U>
130-
typename is_object_manager_help<U>::type
131-
is_object_manager_helper(int*, int*, double, U const&);
187+
typename is_object_manager_help<U>
188+
is_object_manager_helper(U const*, void const*);
132189

133190
template <class U>
134-
typename is_object_manager_help<U>::type
135-
is_object_manager_helper(int*, double, int*, U volatile&);
191+
typename is_object_manager_help<U>
192+
is_object_manager_helper(U volatile*, void volatile*);
136193

137194
template <class U>
138-
typename is_object_manager_help<U>::type
139-
is_object_manager_helper(int*, int*, int*, U const volatile&);
195+
typename is_object_manager_help<U>
196+
is_object_manager_helper(U const volatile*, void const volatile*);
197+
198+
template <class T>
199+
struct is_reference_to_object_manager_nonref
200+
{
201+
BOOST_STATIC_CONSTANT(bool, value = false);
202+
};
140203

141-
no_reference_to_object_manager is_object_manager_helper(...);
204+
template <class T>
205+
struct is_reference_to_object_manager_ref
206+
{
207+
static T sample_object;
208+
BOOST_STATIC_CONSTANT(
209+
bool, value
210+
= (sizeof(is_object_manager_helper(&sample_object, &sample_object).call())
211+
== sizeof(detail::yes_reference_to_object_manager)
212+
)
213+
);
214+
};
142215
}
143216

144217
template <class T>
145218
struct is_reference_to_object_manager
146219
{
147220
typedef typename mpl::select_type<
148-
is_reference<T>::value, int*, double>::type r_t;
149-
typedef typename mpl::select_type<
150-
python::detail::is_reference_to_const<T>::value, int*, double>::type rc_t;
151-
typedef typename mpl::select_type<
152-
python::detail::is_reference_to_volatile<T>::value, int*, double>::type rv_t;
153-
154-
typedef typename mpl::select_type<is_reference<T>::value, T, int>::type value_t;
221+
is_reference<T>::value
222+
, detail::is_reference_to_object_manager_ref<T>
223+
, detail::is_reference_to_object_manager_nonref<T>
224+
> chooser;
155225

156-
static value_t sample_object;
157-
158-
BOOST_STATIC_CONSTANT(
159-
bool, value
160-
= (sizeof(detail::is_object_manager_helper(r_t(),rc_t(),rv_t(),sample_object))
161-
== sizeof(detail::yes_reference_to_object_manager)
162-
)
163-
);
226+
BOOST_STATIC_CONSTANT(bool, value = chooser::type::value);
164227
};
165228
# endif
166229

167-
template <class T>
168-
inline T* get_managed_object(handle<T> const& h)
169-
{
170-
return h.get();
171-
}
172-
173-
template <class T>
174-
inline T* get_managed_object(python::detail::borrowed<T> const volatile* p)
175-
{
176-
return (T*)p;
177-
}
178-
179-
// forward declaration needed because name lookup is bound by the
180-
// definition context.
181-
PyObject* get_managed_object(python::api::object const&);
182-
183230
}}} // namespace boost::python::converter
184231

185232
#endif // OBJECT_MANAGER_DWA2002614_HPP
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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 PYOBJECT_TRAITS_DWA2002720_HPP
7+
# define PYOBJECT_TRAITS_DWA2002720_HPP
8+
9+
# include <boost/python/detail/wrap_python.hpp>
10+
# include <boost/python/converter/pyobject_type.hpp>
11+
12+
namespace boost { namespace python { namespace converter {
13+
14+
template <class> struct pyobject_traits;
15+
16+
template <>
17+
struct pyobject_traits<PyObject>
18+
{
19+
// All objects are convertible to PyObject
20+
static bool check(PyObject*) { return true; }
21+
static PyObject* checked_downcast(PyObject* x) { return x; }
22+
};
23+
24+
//
25+
// Specializations
26+
//
27+
28+
# define BOOST_PYTHON_BUILTIN_OBJECT_TRAITS(T) \
29+
template <> struct pyobject_traits<Py##T##Object> \
30+
: pyobject_type<Py##T##Object, &Py##T##_Type> {}
31+
32+
// This is not an exhaustive list; should be expanded.
33+
BOOST_PYTHON_BUILTIN_OBJECT_TRAITS(Type);
34+
BOOST_PYTHON_BUILTIN_OBJECT_TRAITS(List);
35+
BOOST_PYTHON_BUILTIN_OBJECT_TRAITS(Int);
36+
BOOST_PYTHON_BUILTIN_OBJECT_TRAITS(Long);
37+
BOOST_PYTHON_BUILTIN_OBJECT_TRAITS(Dict);
38+
BOOST_PYTHON_BUILTIN_OBJECT_TRAITS(Tuple);
39+
40+
}}} // namespace boost::python::converter
41+
42+
#endif // PYOBJECT_TRAITS_DWA2002720_HPP

0 commit comments

Comments
 (0)