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 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 
27 /// \file tf/pyPtrHelpers.h
28 /// Enables wrapping of Weak or Ref & Weak held types to python.
29 
30 #include "pxr/pxr.h"
31 
32 #include "pxr/base/tf/pyIdentity.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 <hboost/python/class.hpp>
44 #include <hboost/python/converter/from_python.hpp>
45 #include <hboost/python/converter/registered.hpp>
46 #include <hboost/python/converter/registrations.hpp>
47 #include <hboost/python/converter/registry.hpp>
48 #include <hboost/python/converter/rvalue_from_python_data.hpp>
49 #include <hboost/python/converter/to_python_function_type.hpp>
50 #include <hboost/python/def_visitor.hpp>
51 #include <hboost/python/handle.hpp>
52 #include <hboost/python/implicit.hpp>
53 #include <hboost/python/to_python_converter.hpp>
54 
55 #include <memory>
56 
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 hboost::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(hboost::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 = hboost::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 hboost::python;
113 
114 template <typename Ptr>
116  typedef typename Ptr::DataType Pointee;
119 
120  template <typename U>
121  struct Rebind {
122  typedef typename Ptr::template Rebind<U>::Type Type;
123  };
124 
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 (hboost::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>
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>
167  static void Apply(TfRefPtr<T> const &p, PyObject *obj) {
169  }
170 };
171 
172 template <class Ptr>
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);
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>
212 
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>
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 {
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>
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>
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 hboost/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>
321 
322  static_assert(std::is_same<
324  Wrapper>::value,
325  "Pointee must be same type as Wrapper.");
326 
327  typedef typename
329 
330  // Register the from-python conversion.
332 
333  // Register AnyWeakPtr from python conversion.
335 
336  // From python, can always make a const pointer from a non-const one.
337  implicitly_convertible<PtrType,
339 
340  // Register a conversion that casts away constness when going to python.
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  // hboost 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 hboost 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) {
355  _originalConverter = r->m_to_python;
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 
366 
367  }
368 
369  template <typename PtrType, typename CLS, typename Wrapper, typename T>
370  static void _AddAPI(CLS &c, Wrapper *, T *) {
371  typedef typename
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 *) {
413  typedef typename
415  Rebind<T>::Type 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 
434 
436 
437 #endif // PXR_BASE_TF_PY_PTR_HELPERS_H
static PyObject * convert(Ptr const &p)
Definition: pyPtrHelpers.h:262
GLenum query
Definition: glad.h:2772
hboost::add_const< Pointee >::type ConstPointee
Definition: pyPtrHelpers.h:117
static Result Execute(Ptr const &p)
Definition: pyPtrHelpers.h:85
static void construct(PyObject *source, converter::rvalue_from_python_stage1_data *data)
Definition: pyPtrHelpers.h:227
hboost::remove_const< Pointee >::type NonConstPointee
Definition: pyPtrHelpers.h:118
Rebind< NonConstPointee >::Type NonConstPtr
Definition: pyPtrHelpers.h:126
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
static void _RegisterConversionsHelper()
Definition: pyPtrHelpers.h:320
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:315
**But if you need a result
Definition: thread.h:613
static PyObject * convert(ConstPtr const &p)
Definition: pyPtrHelpers.h:252
_PtrInterface< Ptr >::NonConstPtr NonConstPtr
Definition: pyPtrHelpers.h:248
OIIO_FORCEINLINE vbool4 insert(const vbool4 &a, bool val)
Helper: substitute val for a[i].
Definition: simd.h:3436
Y * get_pointer(TfWeakPtrFacade< X, Y > const &p)
Definition: weakPtrFacade.h:83
static PyObject * Convert(void const *x)
Definition: pyPtrHelpers.h:291
static void Apply(PtrType const &, PyObject *)
Definition: pyPtrHelpers.h:160
#define TF_SUPPORTS_REFPTR(T)
Definition: refPtr.h:1344
#define TF_SUPPORTS_WEAKPTR(T)
Definition: weakPtr.h:439
static converter::to_python_function_t _originalConverter
Definition: pyPtrHelpers.h:288
std::weak_ptr< T > WeakPtr
Definition: Types.h:115
static void _AddAPI(CLS &c, Wrapper *, T *)
Definition: pyPtrHelpers.h:370
Ptr::DataType Pointee
Definition: pyPtrHelpers.h:78
bool _IsPtrValid(object const &self)
Definition: pyPtrHelpers.h:142
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:803
_PtrInterface< Ptr >::ConstPtr ConstPtr
Definition: pyPtrHelpers.h:247
void visit(CLS &c) const
Definition: pyPtrHelpers.h:420
void visit(CLS &c) const
Definition: pyPtrHelpers.h:390
bool _ArePtrsLessThan(PtrType const &self, PtrType const &other)
Definition: pyPtrHelpers.h:153
bool _IsPtrExpired(object const &self)
Definition: pyPtrHelpers.h:131
bool _ArePtrsEqual(PtrType const &self, PtrType const &other)
Definition: pyPtrHelpers.h:147
GLint GLenum GLint x
Definition: glcorearb.h:409
Rebind< ConstPointee >::Type ConstPtr
Definition: pyPtrHelpers.h:125
void Tf_PyRemovePythonOwnership(Ptr const &t, PyObject *obj)
Definition: pyIdentity.h:255
bool _ArePtrsNotEqual(PtrType const &self, PtrType const &other)
Definition: pyPtrHelpers.h:150
GLenum GLenum dst
Definition: glcorearb.h:1793
std::enable_if_t< Tf_PyIsRefPtr< Ptr >::value > Tf_PySetPythonIdentity(Ptr const &, PyObject *)
Definition: pyIdentity.h:231
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
hboost::python::objects::pointer_holder< Ptr, Pointee > Holder
Definition: pyPtrHelpers.h:79
_PtrInterface< Ptr >::Pointee Pointee
Definition: pyPtrHelpers.h:174
static void Apply(TfRefPtr< T > const &p, PyObject *obj)
Definition: pyPtrHelpers.h:167
auto ptr(T p) -> const void *
Definition: format.h:2448
Ptr::template Rebind< U >::Type Type
Definition: pyPtrHelpers.h:122
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
Definition: core.h:1131
PyObject * Tf_PyGetPythonIdentity(Ptr const &ptr)
Definition: pyIdentity.h:248
GLboolean r
Definition: glcorearb.h:1222
static PyObject * convert(SrcPtr const &p)
Definition: pyPtrHelpers.h:276
static void _AddAPI(Wrapper *, T *)
Definition: pyPtrHelpers.h:411
type
Definition: core.h:1059
Definition: format.h:895
std::pair< PyObject *, bool > Result
Definition: pyPtrHelpers.h:80
static void * convertible(PyObject *p)
Definition: pyPtrHelpers.h:218