HDK
 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 terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_TF_PY_PTR_HELPERS_H
8 #define PXR_BASE_TF_PY_PTR_HELPERS_H
9 
10 /// \file tf/pyPtrHelpers.h
11 /// Enables wrapping of Weak or Ref & Weak held types to python.
12 
13 #include "pxr/pxr.h"
14 
15 #include "pxr/base/tf/pyIdentity.h"
18 
19 #include "pxr/base/arch/demangle.h"
20 #include "pxr/base/tf/diagnostic.h"
21 #include "pxr/base/tf/refPtr.h"
23 #include "pxr/base/tf/weakPtr.h"
24 #include "pxr/base/tf/anyWeakPtr.h"
25 
26 #include "pxr/external/boost/python/class.hpp"
27 #include "pxr/external/boost/python/converter/from_python.hpp"
28 #include "pxr/external/boost/python/converter/registered.hpp"
29 #include "pxr/external/boost/python/converter/registrations.hpp"
30 #include "pxr/external/boost/python/converter/registry.hpp"
31 #include "pxr/external/boost/python/converter/rvalue_from_python_data.hpp"
32 #include "pxr/external/boost/python/converter/to_python_function_type.hpp"
33 #include "pxr/external/boost/python/def_visitor.hpp"
34 #include "pxr/external/boost/python/handle.hpp"
35 #include "pxr/external/boost/python/implicit.hpp"
36 #include "pxr/external/boost/python/to_python_converter.hpp"
37 
38 #include <memory>
39 #include <type_traits>
40 
42 
43 //
44 // Boost.Python def visitors for wrapping objects held by weak pointers. This
45 // will create a read-only property on your class called 'expired' which is
46 // true if the object has expired. This also adds an implementation for __eq__
47 // and __ne__ which compare the pointers for equality and non-equality,
48 // respectively.
49 //
50 // Example usage:
51 //
52 // class_<MyClass, MyClassPtr>("MyClass", no_init)
53 // .def(TfPyWeakPtr())
54 // .def(...)
55 // ...
56 //
57 
58 // Helper class to return or create a PyObject holder for a Ptr. This
59 // can be specialized for custom behavior.
60 template <typename Ptr>
61 struct TfMakePyPtr {
62  typedef typename Ptr::DataType Pointee;
63  typedef pxr_boost::python::objects::pointer_holder<Ptr, Pointee> Holder;
64  typedef std::pair<PyObject*, bool> Result;
65 
66  // Return an existing PyObject for the pointer paired with false or
67  // create and return a new PyObject paired with true. The PyObject
68  // ref count must have been incremented.
69  static Result Execute(Ptr const& p)
70  {
71  // null pointers -> python None.
72  if (!p.GetUniqueIdentifier())
73  return Result(pxr_boost::python::detail::none(), false);
74 
75  // Force instantiation. We must do this before checking if we
76  // have a python identity, otherwise the identity might be set
77  // during instantiation and our caller will attempt to set it
78  // again, which isn't allowed.
79  get_pointer(p);
80 
81  if (PyObject *id = Tf_PyGetPythonIdentity(p))
82  return Result(id, false);
83 
84  // Just make a new python object holding this pointer.
85  // TODO: use existing to-python conversion?
86  PyObject *res = pxr_boost::python::objects::make_ptr_instance
87  <Pointee, Holder>::execute(p);
88  // If we got back Py_None, no new object was made, so make sure
89  // to pass back false in result.
90  return Result(res, res != Py_None);
91  }
92 };
93 
94 namespace Tf_PyDefHelpers {
95 
96 using namespace pxr_boost::python;
97 
98 template <typename Ptr>
99 struct _PtrInterface {
100  typedef typename Ptr::DataType Pointee;
101  using ConstPointee = std::add_const_t<Pointee>;
102  using NonConstPointee = std::remove_const_t<Pointee>;
103 
104  template <typename U>
105  struct Rebind {
106  typedef typename Ptr::template Rebind<U>::Type Type;
107  };
108 
111 
112 };
113 
114 template <typename PtrType>
115 bool _IsPtrExpired(object const &self) {
116  try {
117  PtrType p = extract<PtrType>(self);
118  return !p;
119  } catch (pxr_boost::python::error_already_set const &) {
120  PyErr_Clear();
121  return true;
122  }
123 }
124 
125 template <typename PtrType>
126 bool _IsPtrValid(object const &self) {
127  return !_IsPtrExpired<PtrType>(self);
128 }
129 
130 template <typename PtrType>
131 bool _ArePtrsEqual(PtrType const &self,
132  PtrType const &other) { return self == other; }
133 template <typename PtrType>
134 bool _ArePtrsNotEqual(PtrType const &self,
135  PtrType const &other) { return self != other; }
136 template <typename PtrType>
137 bool _ArePtrsLessThan(PtrType const &self,
138  PtrType const &other) { return self < other; }
139 
140 
141 // Default ownership policy does nothing.
142 template <class PtrType>
144  static void Apply(PtrType const &, PyObject *) { }
145 };
146 
147 // Ownership policy for ref ptrs when going from python to c++ is to
148 // transfer ownership (remove ownership from python if it has it).
149 template <typename T>
151  static void Apply(TfRefPtr<T> const &p, PyObject *obj) {
153  }
154 };
155 
156 template <class Ptr>
160  converter::registry::insert(&convertible, &construct,
161  type_id<Ptr>());
162  }
163  private:
164  static void *convertible(PyObject *p) {
165  if (p == Py_None)
166  return p;
167  void *result = converter::get_lvalue_from_python
168  (p, converter::registered<Pointee>::converters);
169  return result;
170  }
171 
172  static void construct(PyObject* source, converter::
173  rvalue_from_python_stage1_data* data) {
174  void* const storage = ((converter::rvalue_from_python_storage<Ptr>*)
175  data)->storage.bytes;
176  // Deal with the "None" case.
177  if (data->convertible == source)
178  new (storage) Ptr();
179  else {
180  Ptr ptr(static_cast<Pointee*>(data->convertible));
181  new (storage) Ptr(ptr);
183  // Set ptr's python object to source if the pointer is valid.
184  if (ptr)
185  Tf_PySetPythonIdentity(ptr, source);
186  }
187  data->convertible = storage;
188  }
189 };
190 
191 // Converter from python to AnyWeakPtr. We use this converter to wrap
192 // the weak-pointable object into an AnyWeakPtr when we don't know what
193 // specific C++ type it has--for example, see wrapNotice.cpp.
194 template <typename PtrType>
196 
198  converter::registry::insert(&convertible, &construct,
199  type_id<TfAnyWeakPtr>());
200  }
201 
202  static void *convertible(PyObject *p) {
203  if (p == Py_None)
204  return p;
205  void *result = converter::get_lvalue_from_python
206  (p, converter::registered
207  <typename _PtrInterface<PtrType>::Pointee>::converters);
208  return result;
209  }
210 
211  static void construct(PyObject* source, converter::
212  rvalue_from_python_stage1_data* data) {
213  void* const storage = ((converter::rvalue_from_python_storage
214  <TfAnyWeakPtr>*)data)->storage.bytes;
215  // Deal with the "None" case.
216  if (data->convertible == source)
217  new (storage) TfAnyWeakPtr();
218  else {
219  typedef typename _PtrInterface<PtrType>::Pointee T;
220  T *ptr = static_cast<T*>(data->convertible);
221  PtrType smartPtr(ptr);
222  new (storage) TfAnyWeakPtr(smartPtr);
223 
224  }
225  data->convertible = storage;
226  }
227 };
228 
229 template <typename Ptr>
234  to_python_converter<ConstPtr, _ConstPtrToPython<Ptr> >();
235  }
236  static PyObject *convert(ConstPtr const &p) {
237  return incref(object(TfConst_cast<NonConstPtr>(p)).ptr());
238  }
239 };
240 
241 template <typename Ptr>
242 struct _PtrToPython {
244  to_python_converter<Ptr, _PtrToPython<Ptr> >();
245  }
246  static PyObject *convert(Ptr const &p) {
247  std::pair<PyObject*, bool> ret = TfMakePyPtr<Ptr>::Execute(p);
248  if (ret.second) {
249  Tf_PySetPythonIdentity(p, ret.first);
250  }
251  return ret.first;
252  }
253 };
254 
255 template <typename SrcPtr, typename DstPtr>
258  to_python_converter<SrcPtr, _ConvertPtrToPython<SrcPtr, DstPtr> >();
259  }
260  static PyObject *convert(SrcPtr const &p) {
261  DstPtr dst = p;
262  return incref(object(dst).ptr());
263  }
264 };
265 
266 template <typename Ptr>
268 
269  // We store the original to-python converter for our use. It's fine to be
270  // static, as there's only one to-python converter for a type T, and there's
271  // one instantiation of this template for each T.
272  static converter::to_python_function_t _originalConverter;
273 
274  // This signature has to match to_python_function_t
275  static PyObject *Convert(void const *x) {
276  // See pxr/external/boost/python/converter/as_to_python_function.hpp
277  Ptr const &p = *static_cast<Ptr const *>(x);
278 
279  std::pair<PyObject*, bool> ret = TfMakePyPtr<Ptr>::Execute(p);
280  if (ret.first == Py_None) {
281  // Fallback to the original converter.
282  Py_DECREF(ret.first);
283  ret.first = _originalConverter(x);
284  }
285  if (ret.second) {
286  Tf_PySetPythonIdentity(p, ret.first);
287  }
288  return ret.first;
289  }
290 };
291 template <typename T>
292 converter::to_python_function_t
293 _PtrToPythonWrapper<T>::_originalConverter = 0;
294 
295 struct WeakPtr : def_visitor<WeakPtr> {
296  friend class def_visitor_access;
297 
298  template <typename WrapperPtrType, typename Wrapper, typename T>
299  static void _RegisterConversions(Wrapper *, T *) {
300  _RegisterConversionsHelper<WrapperPtrType, Wrapper, T>();
301  }
302 
303  template <typename WrapperPtrType, typename Wrapper, typename T>
305 
306  static_assert(std::is_same<
308  Wrapper>::value,
309  "Pointee must be same type as Wrapper.");
310 
311  typedef typename
313 
314  // Register the from-python conversion.
316 
317  // Register AnyWeakPtr from python conversion.
319 
320  // From python, can always make a const pointer from a non-const one.
321  implicitly_convertible<PtrType,
323 
324  // Register a conversion that casts away constness when going to python.
326 
327  // Replace the existing to_python conversion for weakptr<wrapper> to do
328  // object id first. It would be great if we could get better support in
329  // boost python for doing this sort of thing.
330  //
331  // We do this for wrapper because this is the "wrapped type" -- the type
332  // for which boost python has already registered a to-python
333  // conversion. The unwrapped type is handled separately -- we don't
334  // have to replace an existing converter, we can just register our own.
335  converter::registration *r = const_cast<converter::registration *>
336  (converter::registry::query(type_id<WrapperPtrType>()));
337  if (r) {
339  _originalConverter = r->m_to_python;
341  } else {
342  // CODE_COVERAGE_OFF Can only happen if there's a bug.
343  TF_CODING_ERROR("No python registration for '%s'!",
344  ArchGetDemangled(typeid(WrapperPtrType)).c_str());
345  // CODE_COVERAGE_ON
346  }
347 
350 
351  }
352 
353  template <typename PtrType, typename CLS, typename Wrapper, typename T>
354  static void _AddAPI(CLS &c, Wrapper *, T *) {
355  typedef typename
357  // Add 'expired' property and (in)equality testing.
358  c.add_property("expired", _IsPtrExpired<UnwrappedPtrType>,
359  (const char *)
360  "True if this object has expired, False otherwise.");
361  c.def("__bool__", _IsPtrValid<UnwrappedPtrType>,
362  (char const *)
363  "True if this object has not expired. False otherwise.");
364  c.def("__eq__", _ArePtrsEqual<UnwrappedPtrType>,
365  "Equality operator: x == y");
366  c.def("__ne__", _ArePtrsNotEqual<UnwrappedPtrType>,
367  "Non-equality operator: x != y");
368  c.def("__lt__", _ArePtrsLessThan<UnwrappedPtrType>,
369  "Less than operator: x < y");
370  c.def( TfTypePythonClass() );
371  }
372 
373  template <typename CLS>
374  void visit(CLS &c) const {
375  typedef typename CLS::wrapped_type Type;
376  typedef typename CLS::metadata::held_type_arg PtrType;
377  static_assert(TF_SUPPORTS_WEAKPTR(Type),
378  "Type must support TfWeakPtr.");
379  // Register conversions
380  _RegisterConversions<PtrType>
381  ((Type *)0, detail::unwrap_wrapper((Type *)0));
382 
383  // Register a PyObjectFinder.
384  Tf_RegisterPythonObjectFinder<Type, PtrType>();
385 
386  // Add weak ptr api.
387  _AddAPI<PtrType>(c, (Type *)0, detail::unwrap_wrapper((Type *)0));
388  }
389 };
390 
391 struct RefAndWeakPtr : def_visitor<RefAndWeakPtr> {
392  friend class def_visitor_access;
393 
394  template <typename CLS, typename Wrapper, typename T>
395  static void _AddAPI(Wrapper *, T *) {
397  typedef typename
399  Rebind<T>::Type PtrType;
401  }
402 
403  template <typename CLS>
404  void visit(CLS &c) const {
405  typedef typename CLS::wrapped_type Type;
406  static_assert(TF_SUPPORTS_REFPTR(Type),
407  "Type must support TfRefPtr.");
408  // Same as weak ptr plus ref conversions.
409  WeakPtr().visit(c);
410  _AddAPI<CLS>((Type *)0, detail::unwrap_wrapper((Type *)0));
411  }
412 };
413 
414 };
415 
418 
420 
421 #endif // PXR_BASE_TF_PY_PTR_HELPERS_H
static PyObject * convert(Ptr const &p)
Definition: pyPtrHelpers.h:246
GLenum query
Definition: glad.h:2772
bool none(const vbool4 &v)
Definition: simd.h:3601
static Result Execute(Ptr const &p)
Definition: pyPtrHelpers.h:69
static void construct(PyObject *source, converter::rvalue_from_python_stage1_data *data)
Definition: pyPtrHelpers.h:211
Rebind< NonConstPointee >::Type NonConstPtr
Definition: pyPtrHelpers.h:110
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
static void _RegisterConversionsHelper()
Definition: pyPtrHelpers.h:304
std::remove_const_t< Pointee > NonConstPointee
Definition: pyPtrHelpers.h:102
std::add_const_t< Pointee > ConstPointee
Definition: pyPtrHelpers.h:101
GLsizei const GLfloat * value
Definition: glcorearb.h:824
#define TF_CODING_ERROR
ARCH_API std::string ArchGetDemangled(const std::string &typeName)
static void _RegisterConversions(Wrapper *, T *)
Definition: pyPtrHelpers.h:299
**But if you need a result
Definition: thread.h:622
static PyObject * convert(ConstPtr const &p)
Definition: pyPtrHelpers.h:236
_PtrInterface< Ptr >::NonConstPtr NonConstPtr
Definition: pyPtrHelpers.h:232
OIIO_FORCEINLINE vbool4 insert(const vbool4 &a, bool val)
Helper: substitute val for a[i].
Definition: simd.h:3556
Y * get_pointer(TfWeakPtrFacade< X, Y > const &p)
Definition: weakPtrFacade.h:63
static PyObject * Convert(void const *x)
Definition: pyPtrHelpers.h:275
static void Apply(PtrType const &, PyObject *)
Definition: pyPtrHelpers.h:144
#define TF_SUPPORTS_REFPTR(T)
Definition: refPtr.h:1318
#define TF_SUPPORTS_WEAKPTR(T)
Definition: weakPtr.h:422
static converter::to_python_function_t _originalConverter
Definition: pyPtrHelpers.h:272
std::weak_ptr< T > WeakPtr
Definition: Types.h:115
static void _AddAPI(CLS &c, Wrapper *, T *)
Definition: pyPtrHelpers.h:354
Ptr::DataType Pointee
Definition: pyPtrHelpers.h:62
bool _IsPtrValid(object const &self)
Definition: pyPtrHelpers.h:126
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:803
_PtrInterface< Ptr >::ConstPtr ConstPtr
Definition: pyPtrHelpers.h:231
void visit(CLS &c) const
Definition: pyPtrHelpers.h:404
void visit(CLS &c) const
Definition: pyPtrHelpers.h:374
bool _ArePtrsLessThan(PtrType const &self, PtrType const &other)
Definition: pyPtrHelpers.h:137
bool _IsPtrExpired(object const &self)
Definition: pyPtrHelpers.h:115
bool _ArePtrsEqual(PtrType const &self, PtrType const &other)
Definition: pyPtrHelpers.h:131
GLint GLenum GLint x
Definition: glcorearb.h:409
pxr_boost::python::objects::pointer_holder< Ptr, Pointee > Holder
Definition: pyPtrHelpers.h:63
Rebind< ConstPointee >::Type ConstPtr
Definition: pyPtrHelpers.h:109
void Tf_PyRemovePythonOwnership(Ptr const &t, PyObject *obj)
Definition: pyIdentity.h:220
bool _ArePtrsNotEqual(PtrType const &self, PtrType const &other)
Definition: pyPtrHelpers.h:134
GLenum GLenum dst
Definition: glcorearb.h:1793
std::enable_if_t< Tf_PyIsRefPtr< Ptr >::value > Tf_PySetPythonIdentity(Ptr const &, PyObject *)
Definition: pyIdentity.h:196
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
_PtrInterface< Ptr >::Pointee Pointee
Definition: pyPtrHelpers.h:158
static void Apply(TfRefPtr< T > const &p, PyObject *obj)
Definition: pyPtrHelpers.h:151
auto ptr(T p) -> const void *
Definition: format.h:4331
Ptr::template Rebind< U >::Type Type
Definition: pyPtrHelpers.h:106
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
OIIO_UTIL_API const char * c_str(string_view str)
PyObject * Tf_PyGetPythonIdentity(Ptr const &ptr)
Definition: pyIdentity.h:213
GLboolean r
Definition: glcorearb.h:1222
static PyObject * convert(SrcPtr const &p)
Definition: pyPtrHelpers.h:260
static void _AddAPI(Wrapper *, T *)
Definition: pyPtrHelpers.h:395
Definition: format.h:1821
std::pair< PyObject *, bool > Result
Definition: pyPtrHelpers.h:64
static void * convertible(PyObject *p)
Definition: pyPtrHelpers.h:202