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