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