pyPtrHelpers.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_PY_PTR_HELPERS_H
25 #define PXR_BASE_TF_PY_PTR_HELPERS_H
26 
29 
30 #include "pxr/pxr.h"
31 
32 #include "pxr/base/tf/pyIdentity.h"
33 #include "pxr/base/tf/pyObjectFinder.h"
34 #include "pxr/base/tf/wrapTypeHelpers.h"
35 
36 #include "pxr/base/arch/demangle.h"
37 #include "pxr/base/tf/diagnostic.h"
38 #include "pxr/base/tf/refPtr.h"
40 #include "pxr/base/tf/weakPtr.h"
41 #include "pxr/base/tf/anyWeakPtr.h"
42 
43 #include <boost/python/class.hpp>
44 #include <boost/python/converter/from_python.hpp>
45 #include <boost/python/converter/registered.hpp>
46 #include <boost/python/converter/registrations.hpp>
47 #include <boost/python/converter/registry.hpp>
48 #include <boost/python/converter/rvalue_from_python_data.hpp>
49 #include <boost/python/converter/to_python_function_type.hpp>
50 #include <boost/python/def_visitor.hpp>
51 #include <boost/python/handle.hpp>
52 #include <boost/python/implicit.hpp>
53 #include <boost/python/to_python_converter.hpp>
54 
55 #include <memory>
56 
57 PXR_NAMESPACE_OPEN_SCOPE
58 
59 //
60 // Boost.Python def visitors for wrapping objects held by weak pointers. This
61 // will create a read-only property on your class called 'expired' which is
62 // true if the object has expired. This also adds an implementation for __eq__
63 // and __ne__ which compare the pointers for equality and non-equality,
64 // respectively.
65 //
66 // Example usage:
67 //
68 // class_<MyClass, MyClassPtr>("MyClass", no_init)
69 // .def(TfPyWeakPtr())
70 // .def(...)
71 // ...
72 //
73 
74 // Helper class to return or create a PyObject holder for a Ptr. This
75 // can be specialized for custom behavior.
76 template <typename Ptr>
77 struct TfMakePyPtr {
78  typedef typename Ptr::DataType Pointee;
79  typedef boost::python::objects::pointer_holder<Ptr, Pointee> Holder;
80  typedef std::pair<PyObject*, bool> Result;
81 
82  // Return an existing PyObject for the pointer paired with false or
83  // create and return a new PyObject paired with true. The PyObject
84  // ref count must have been incremented.
85  static Result Execute(Ptr const& p)
86  {
87  // null pointers -> python None.
88  if (!p.GetUniqueIdentifier())
89  return Result(boost::python::detail::none(), false);
90 
91  // Force instantiation. We must do this before checking if we
92  // have a python identity, otherwise the identity might be set
93  // during instantiation and our caller will attempt to set it
94  // again, which isn't allowed.
95  get_pointer(p);
96 
97  if (PyObject *id = Tf_PyGetPythonIdentity(p))
98  return Result(id, false);
99 
100  // Just make a new python object holding this pointer.
101  // TODO: use existing to-python conversion?
102  PyObject *res = boost::python::objects::make_ptr_instance
103  <Pointee, Holder>::execute(p);
104  // If we got back Py_None, no new object was made, so make sure
105  // to pass back false in result.
106  return Result(res, res != Py_None);
107  }
108 };
109 
110 namespace Tf_PyDefHelpers {
111 
112 using namespace boost::python;
113 
114 template <typename Ptr>
115 struct _PtrInterface {
116  typedef typename Ptr::DataType Pointee;
117  typedef typename boost::add_const<Pointee>::type ConstPointee;
118  typedef typename boost::remove_const<Pointee>::type NonConstPointee;
119 
120  template <typename U>
121  struct Rebind {
122  typedef typename Ptr::template Rebind<U>::Type Type;
123  };
124 
125  typedef typename Rebind<ConstPointee>::Type ConstPtr;
126  typedef typename Rebind<NonConstPointee>::Type NonConstPtr;
127 
128 };
129 
130 template <typename PtrType>
131 bool _IsPtrExpired(object const &self) {
132  try {
133  PtrType p = extract<PtrType>(self);
134  return !p;
135  } catch (boost::python::error_already_set const &) {
136  PyErr_Clear();
137  return true;
138  }
139 }
140 
141 template <typename PtrType>
142 bool _IsPtrValid(object const &self) {
143  return !_IsPtrExpired<PtrType>(self);
144 }
145 
146 template <typename PtrType>
147 bool _ArePtrsEqual(PtrType const &self,
148  PtrType const &other) { return self == other; }
149 template <typename PtrType>
150 bool _ArePtrsNotEqual(PtrType const &self,
151  PtrType const &other) { return self != other; }
152 template <typename PtrType>
153 bool _ArePtrsLessThan(PtrType const &self,
154  PtrType const &other) { return self < other; }
155 
156 
157 // Default ownership policy does nothing.
158 template <class PtrType>
159 struct _PtrFromPythonConversionPolicy {
160  static void Apply(PtrType const &, PyObject *) { }
161 };
162 
163 // Ownership policy for ref ptrs when going from python to c++ is to
164 // transfer ownership (remove ownership from python if it has it).
165 template <typename T>
166 struct _PtrFromPythonConversionPolicy<TfRefPtr<T> > {
167  static void Apply(TfRefPtr<T> const &p, PyObject *obj) {
168  Tf_PyRemovePythonOwnership(p, obj);
169  }
170 };
171 
172 template <class Ptr>
173 struct _PtrFromPython {
174  typedef typename _PtrInterface<Ptr>::Pointee Pointee;
175  _PtrFromPython() {
176  converter::registry::insert(&convertible, &construct,
177  type_id<Ptr>());
178  }
179  private:
180  static void *convertible(PyObject *p) {
181  if (p == Py_None)
182  return p;
183  void *result = converter::get_lvalue_from_python
184  (p, converter::registered<Pointee>::converters);
185  return result;
186  }
187 
188  static void construct(PyObject* source, converter::
189  rvalue_from_python_stage1_data* data) {
190  void* const storage = ((converter::rvalue_from_python_storage<Ptr>*)
191  data)->storage.bytes;
192  // Deal with the "None" case.
193  if (data->convertible == source)
194  new (storage) Ptr();
195  else {
196  Ptr ptr(static_cast<Pointee*>(data->convertible));
197  new (storage) Ptr(ptr);
198  _PtrFromPythonConversionPolicy<Ptr>::Apply(ptr, source);
199  // Set ptr's python object to source if the pointer is valid.
200  if (ptr)
201  Tf_PySetPythonIdentity(ptr, source);
202  }
203  data->convertible = storage;
204  }
205 };
206 
207 // Converter from python to AnyWeakPtr. We use this converter to wrap
208 // the weak-pointable object into an AnyWeakPtr when we don't know what
209 // specific C++ type it has--for example, see wrapNotice.cpp.
210 template <typename PtrType>
211 struct _AnyWeakPtrFromPython {
212 
213  _AnyWeakPtrFromPython() {
214  converter::registry::insert(&convertible, &construct,
215  type_id<TfAnyWeakPtr>());
216  }
217 
218  static void *convertible(PyObject *p) {
219  if (p == Py_None)
220  return p;
221  void *result = converter::get_lvalue_from_python
222  (p, converter::registered
223  <typename _PtrInterface<PtrType>::Pointee>::converters);
224  return result;
225  }
226 
227  static void construct(PyObject* source, converter::
228  rvalue_from_python_stage1_data* data) {
229  void* const storage = ((converter::rvalue_from_python_storage
230  <TfAnyWeakPtr>*)data)->storage.bytes;
231  // Deal with the "None" case.
232  if (data->convertible == source)
233  new (storage) TfAnyWeakPtr();
234  else {
235  typedef typename _PtrInterface<PtrType>::Pointee T;
236  T *ptr = static_cast<T*>(data->convertible);
237  PtrType smartPtr(ptr);
238  new (storage) TfAnyWeakPtr(smartPtr);
239 
240  }
241  data->convertible = storage;
242  }
243 };
244 
245 template <typename Ptr>
246 struct _ConstPtrToPython {
247  typedef typename _PtrInterface<Ptr>::ConstPtr ConstPtr;
248  typedef typename _PtrInterface<Ptr>::NonConstPtr NonConstPtr;
249  _ConstPtrToPython() {
250  to_python_converter<ConstPtr, _ConstPtrToPython<Ptr> >();
251  }
252  static PyObject *convert(ConstPtr const &p) {
253  return incref(object(TfConst_cast<NonConstPtr>(p)).ptr());
254  }
255 };
256 
257 template <typename Ptr>
258 struct _PtrToPython {
259  _PtrToPython() {
260  to_python_converter<Ptr, _PtrToPython<Ptr> >();
261  }
262  static PyObject *convert(Ptr const &p) {
263  std::pair<PyObject*, bool> ret = TfMakePyPtr<Ptr>::Execute(p);
264  if (ret.second) {
265  Tf_PySetPythonIdentity(p, ret.first);
266  }
267  return ret.first;
268  }
269 };
270 
271 template <typename SrcPtr, typename DstPtr>
272 struct _ConvertPtrToPython {
273  _ConvertPtrToPython() {
274  to_python_converter<SrcPtr, _ConvertPtrToPython<SrcPtr, DstPtr> >();
275  }
276  static PyObject *convert(SrcPtr const &p) {
277  DstPtr dst = p;
278  return incref(object(dst).ptr());
279  }
280 };
281 
282 template <typename Ptr>
283 struct _PtrToPythonWrapper {
284 
285  // We store the original to-python converter for our use. It's fine to be
286  // static, as there's only one to-python converter for a type T, and there's
287  // one instantiation of this template for each T.
288  static converter::to_python_function_t _originalConverter;
289 
290  // This signature has to match to_python_function_t
291  static PyObject *Convert(void const *x) {
292  // See boost/python/converter/as_to_python_function.hpp
293  Ptr const &p = *static_cast<Ptr const *>(x);
294 
295  std::pair<PyObject*, bool> ret = TfMakePyPtr<Ptr>::Execute(p);
296  if (ret.first == Py_None) {
297  // Fallback to the original converter.
298  Py_DECREF(ret.first);
299  ret.first = _originalConverter(x);
300  }
301  if (ret.second) {
302  Tf_PySetPythonIdentity(p, ret.first);
303  }
304  return ret.first;
305  }
306 };
307 template <typename T>
308 converter::to_python_function_t
309 _PtrToPythonWrapper<T>::_originalConverter = 0;
310 
311 struct WeakPtr : def_visitor<WeakPtr> {
312  friend class def_visitor_access;
313 
314  template <typename WrapperPtrType, typename Wrapper, typename T>
315  static void _RegisterConversions(Wrapper *, T *) {
316  _RegisterConversionsHelper<WrapperPtrType, Wrapper, T>();
317  }
318 
319  template <typename WrapperPtrType, typename Wrapper, typename T>
320  static void _RegisterConversionsHelper() {
321 
322  static_assert(std::is_same<
323  typename _PtrInterface<WrapperPtrType>::Pointee,
324  Wrapper>::value,
325  "Pointee must be same type as Wrapper.");
326 
327  typedef typename
328  _PtrInterface<WrapperPtrType>::template Rebind<T>::Type PtrType;
329 
330  // Register the from-python conversion.
331  _PtrFromPython<PtrType>();
332 
333  // Register AnyWeakPtr from python conversion.
334  _AnyWeakPtrFromPython<PtrType>();
335 
336  // From python, can always make a const pointer from a non-const one.
337  implicitly_convertible<PtrType,
338  typename _PtrInterface<PtrType>::ConstPtr >();
339 
340  // Register a conversion that casts away constness when going to python.
341  _ConstPtrToPython<PtrType>();
342 
343  // Replace the existing to_python conversion for weakptr<wrapper> to do
344  // object id first. It would be great if we could get better support in
345  // boost python for doing this sort of thing.
346  //
347  // We do this for wrapper because this is the "wrapped type" -- the type
348  // for which boost python has already registered a to-python
349  // conversion. The unwrapped type is handled separately -- we don't
350  // have to replace an existing converter, we can just register our own.
351  converter::registration *r = const_cast<converter::registration *>
352  (converter::registry::query(type_id<WrapperPtrType>()));
353  if (r) {
354  _PtrToPythonWrapper<WrapperPtrType>::
355  _originalConverter = r->m_to_python;
356  r->m_to_python = _PtrToPythonWrapper<WrapperPtrType>::Convert;
357  } else {
358  // CODE_COVERAGE_OFF Can only happen if there's a bug.
359  TF_CODING_ERROR("No python registration for '%s'!",
360  ArchGetDemangled(typeid(WrapperPtrType)).c_str());
361  // CODE_COVERAGE_ON
362  }
363 
364  if (!std::is_same<Wrapper, T>::value)
365  _PtrToPython<PtrType>();
366 
367  }
368 
369  template <typename PtrType, typename CLS, typename Wrapper, typename T>
370  static void _AddAPI(CLS &c, Wrapper *, T *) {
371  typedef typename
372  _PtrInterface<PtrType>::template Rebind<T>::Type UnwrappedPtrType;
373  // Add 'expired' property and (in)equality testing.
374  c.add_property("expired", _IsPtrExpired<UnwrappedPtrType>,
375  (const char *)
376  "True if this object has expired, False otherwise.");
377  c.def("__bool__", _IsPtrValid<UnwrappedPtrType>,
378  (char const *)
379  "True if this object has not expired. False otherwise.");
380  c.def("__eq__", _ArePtrsEqual<UnwrappedPtrType>,
381  "Equality operator: x == y");
382  c.def("__ne__", _ArePtrsNotEqual<UnwrappedPtrType>,
383  "Non-equality operator: x != y");
384  c.def("__lt__", _ArePtrsLessThan<UnwrappedPtrType>,
385  "Less than operator: x < y");
386  c.def( TfTypePythonClass() );
387  }
388 
389  template <typename CLS>
390  void visit(CLS &c) const {
391  typedef typename CLS::wrapped_type Type;
392  typedef typename CLS::metadata::held_type_arg PtrType;
393  static_assert(TF_SUPPORTS_WEAKPTR(Type),
394  "Type must support TfWeakPtr.");
395  // Register conversions
396  _RegisterConversions<PtrType>
397  ((Type *)0, detail::unwrap_wrapper((Type *)0));
398 
399  // Register a PyObjectFinder.
400  Tf_RegisterPythonObjectFinder<Type, PtrType>();
401 
402  // Add weak ptr api.
403  _AddAPI<PtrType>(c, (Type *)0, detail::unwrap_wrapper((Type *)0));
404  }
405 };
406 
407 struct RefAndWeakPtr : def_visitor<RefAndWeakPtr> {
408  friend class def_visitor_access;
409 
410  template <typename CLS, typename Wrapper, typename T>
411  static void _AddAPI(Wrapper *, T *) {
412  _PtrFromPython<TfRefPtr<T> >();
413  typedef typename
414  _PtrInterface<typename CLS::metadata::held_type>::template
415  Rebind<T>::Type PtrType;
416  _ConvertPtrToPython<TfRefPtr<T>, PtrType>();
417  }
418 
419  template <typename CLS>
420  void visit(CLS &c) const {
421  typedef typename CLS::wrapped_type Type;
422  static_assert(TF_SUPPORTS_REFPTR(Type),
423  "Type must support TfRefPtr.");
424  // Same as weak ptr plus ref conversions.
425  WeakPtr().visit(c);
426  _AddAPI<CLS>((Type *)0, detail::unwrap_wrapper((Type *)0));
427  }
428 };
429 
430 };
431 
432 struct TfPyWeakPtr : Tf_PyDefHelpers::WeakPtr {};
433 struct TfPyRefAndWeakPtr : Tf_PyDefHelpers::RefAndWeakPtr {};
434 
435 PXR_NAMESPACE_CLOSE_SCOPE
436 
437 #endif // PXR_BASE_TF_PY_PTR_HELPERS_H
A boost.python visitor that associates the Python class object created by the wrapping with the TfTyp...
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:85
ARCH_API std::string ArchGetDemangled(const std::string &typeName)
Return demangled RTTI-generated type name.
Type independent WeakPtr holder class.
Definitions of basic string utilities in tf.
Low-level utilities for informing users of various internal and external diagnostic conditions.
Pointer storage with deletion detection.
Demangle C++ typenames generated by the typeid() facility.
Provides the ability to hold an arbitrary TfWeakPtr in a non-type-specific manner in order to observe...
Definition: anyWeakPtr.h:54
Reference counting.
Reference-counted smart pointer utility class.
Definition: refBase.h:37