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 
29 
30 #include "pxr/pxr.h"
31 
32 #include "pxr/base/tf/py3Compat.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"
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 
56 #include <memory>
57 
58 PXR_NAMESPACE_OPEN_SCOPE
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 boost::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(boost::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 = boost::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 boost::python;
114 
115 template <typename Ptr>
116 struct _PtrInterface {
117  typedef typename Ptr::DataType Pointee;
118  typedef typename boost::add_const<Pointee>::type ConstPointee;
119  typedef typename boost::remove_const<Pointee>::type NonConstPointee;
120 
121  template <typename U>
122  struct Rebind {
123  typedef typename Ptr::template Rebind<U>::Type Type;
124  };
125 
126  typedef typename Rebind<ConstPointee>::Type ConstPtr;
127  typedef typename Rebind<NonConstPointee>::Type NonConstPtr;
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 (boost::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 
154 // Default ownership policy does nothing.
155 template <class PtrType>
156 struct _PtrFromPythonConversionPolicy {
157  static void Apply(PtrType const &, PyObject *) { }
158 };
159 
160 // Ownership policy for ref ptrs when going from python to c++ is to
161 // transfer ownership (remove ownership from python if it has it).
162 template <typename T>
163 struct _PtrFromPythonConversionPolicy<TfRefPtr<T> > {
164  static void Apply(TfRefPtr<T> const &p, PyObject *obj) {
165  Tf_PyRemovePythonOwnership(p, obj);
166  }
167 };
168 
169 template <class Ptr>
170 struct _PtrFromPython {
171  typedef typename _PtrInterface<Ptr>::Pointee Pointee;
172  _PtrFromPython() {
173  converter::registry::insert(&convertible, &construct,
174  type_id<Ptr>());
175  }
176  private:
177  static void *convertible(PyObject *p) {
178  if (p == Py_None)
179  return p;
180  void *result = converter::get_lvalue_from_python
181  (p, converter::registered<Pointee>::converters);
182  return result;
183  }
184 
185  static void construct(PyObject* source, converter::
186  rvalue_from_python_stage1_data* data) {
187  void* const storage = ((converter::rvalue_from_python_storage<Ptr>*)
188  data)->storage.bytes;
189  // Deal with the "None" case.
190  if (data->convertible == source)
191  new (storage) Ptr();
192  else {
193  Ptr ptr(static_cast<Pointee*>(data->convertible));
194  new (storage) Ptr(ptr);
195  _PtrFromPythonConversionPolicy<Ptr>::Apply(ptr, source);
196  // Set ptr's python object to source if the pointer is valid.
197  if (ptr)
198  Tf_PySetPythonIdentity(ptr, source);
199  }
200  data->convertible = storage;
201  }
202 };
203 
204 // Converter from python to AnyWeakPtr. We use this converter to wrap
205 // the weak-pointable object into an AnyWeakPtr when we don't know what
206 // specific C++ type it has--for example, see wrapNotice.cpp.
207 template <typename PtrType>
208 struct _AnyWeakPtrFromPython {
209 
210  _AnyWeakPtrFromPython() {
211  converter::registry::insert(&convertible, &construct,
212  type_id<TfAnyWeakPtr>());
213  }
214 
215  static void *convertible(PyObject *p) {
216  if (p == Py_None)
217  return p;
218  void *result = converter::get_lvalue_from_python
219  (p, converter::registered
220  <typename _PtrInterface<PtrType>::Pointee>::converters);
221  return result;
222  }
223 
224  static void construct(PyObject* source, converter::
225  rvalue_from_python_stage1_data* data) {
226  void* const storage = ((converter::rvalue_from_python_storage
227  <TfAnyWeakPtr>*)data)->storage.bytes;
228  // Deal with the "None" case.
229  if (data->convertible == source)
230  new (storage) TfAnyWeakPtr();
231  else {
232  typedef typename _PtrInterface<PtrType>::Pointee T;
233  T *ptr = static_cast<T*>(data->convertible);
234  PtrType smartPtr(ptr);
235  new (storage) TfAnyWeakPtr(smartPtr);
236 
237  }
238  data->convertible = storage;
239  }
240 };
241 
242 template <typename Ptr>
243 struct _ConstPtrToPython {
244  typedef typename _PtrInterface<Ptr>::ConstPtr ConstPtr;
245  typedef typename _PtrInterface<Ptr>::NonConstPtr NonConstPtr;
246  _ConstPtrToPython() {
247  to_python_converter<ConstPtr, _ConstPtrToPython<Ptr> >();
248  }
249  static PyObject *convert(ConstPtr const &p) {
250  return incref(object(TfConst_cast<NonConstPtr>(p)).ptr());
251  }
252 };
253 
254 template <typename Ptr>
255 struct _PtrToPython {
256  _PtrToPython() {
257  to_python_converter<Ptr, _PtrToPython<Ptr> >();
258  }
259  static PyObject *convert(Ptr const &p) {
260  std::pair<PyObject*, bool> ret = TfMakePyPtr<Ptr>::Execute(p);
261  if (ret.second) {
262  Tf_PySetPythonIdentity(p, ret.first);
263  }
264  return ret.first;
265  }
266 };
267 
268 template <typename SrcPtr, typename DstPtr>
269 struct _ConvertPtrToPython {
270  _ConvertPtrToPython() {
271  to_python_converter<SrcPtr, _ConvertPtrToPython<SrcPtr, DstPtr> >();
272  }
273  static PyObject *convert(SrcPtr const &p) {
274  DstPtr dst = p;
275  return incref(object(dst).ptr());
276  }
277 };
278 
279 template <typename Ptr>
280 struct _PtrToPythonWrapper {
281 
282  // We store the original to-python converter for our use. It's fine to be
283  // static, as there's only one to-python converter for a type T, and there's
284  // one instantiation of this template for each T.
285  static converter::to_python_function_t _originalConverter;
286 
287  // This signature has to match to_python_function_t
288  static PyObject *Convert(void const *x) {
289  // See boost/python/converter/as_to_python_function.hpp
290  Ptr const &p = *static_cast<Ptr const *>(x);
291 
292  std::pair<PyObject*, bool> ret = TfMakePyPtr<Ptr>::Execute(p);
293  if (ret.first == Py_None) {
294  // Fallback to the original converter.
295  Py_DECREF(ret.first);
296  ret.first = _originalConverter(x);
297  }
298  if (ret.second) {
299  Tf_PySetPythonIdentity(p, ret.first);
300  }
301  return ret.first;
302  }
303 };
304 template <typename T>
305 converter::to_python_function_t
306 _PtrToPythonWrapper<T>::_originalConverter = 0;
307 
308 struct WeakPtr : def_visitor<WeakPtr> {
309  friend class def_visitor_access;
310 
311  template <typename WrapperPtrType, typename Wrapper, typename T>
312  static void _RegisterConversions(Wrapper *, T *) {
313  _RegisterConversionsHelper<WrapperPtrType, Wrapper, T>();
314  }
315 
316  template <typename WrapperPtrType, typename Wrapper, typename T>
317  static void _RegisterConversionsHelper() {
318 
319  static_assert(std::is_same<
320  typename _PtrInterface<WrapperPtrType>::Pointee,
321  Wrapper>::value,
322  "Pointee must be same type as Wrapper.");
323 
324  typedef typename
325  _PtrInterface<WrapperPtrType>::template Rebind<T>::Type PtrType;
326 
327  // Register the from-python conversion.
328  _PtrFromPython<PtrType>();
329 
330  // Register AnyWeakPtr from python conversion.
331  _AnyWeakPtrFromPython<PtrType>();
332 
333  // From python, can always make a const pointer from a non-const one.
334  implicitly_convertible<PtrType,
335  typename _PtrInterface<PtrType>::ConstPtr >();
336 
337  // Register a conversion that casts away constness when going to python.
338  _ConstPtrToPython<PtrType>();
339 
340  // Replace the existing to_python conversion for weakptr<wrapper> to do
341  // object id first. It would be great if we could get better support in
342  // boost python for doing this sort of thing.
343  //
344  // We do this for wrapper because this is the "wrapped type" -- the type
345  // for which boost python has already registered a to-python
346  // conversion. The unwrapped type is handled separately -- we don't
347  // have to replace an existing converter, we can just register our own.
348  converter::registration *r = const_cast<converter::registration *>
349  (converter::registry::query(type_id<WrapperPtrType>()));
350  if (r) {
351  _PtrToPythonWrapper<WrapperPtrType>::
352  _originalConverter = r->m_to_python;
353  r->m_to_python = _PtrToPythonWrapper<WrapperPtrType>::Convert;
354  } else {
355  // CODE_COVERAGE_OFF Can only happen if there's a bug.
356  TF_CODING_ERROR("No python registration for '%s'!",
357  ArchGetDemangled(typeid(WrapperPtrType)).c_str());
358  // CODE_COVERAGE_ON
359  }
360 
361  if (!std::is_same<Wrapper, T>::value)
362  _PtrToPython<PtrType>();
363 
364  }
365 
366  template <typename PtrType, typename CLS, typename Wrapper, typename T>
367  static void _AddAPI(CLS &c, Wrapper *, T *) {
368  typedef typename
369  _PtrInterface<PtrType>::template Rebind<T>::Type UnwrappedPtrType;
370  // Add 'expired' property and (in)equality testing.
371  c.add_property("expired", _IsPtrExpired<UnwrappedPtrType>,
372  (const char *)
373  "True if this object has expired, False otherwise.");
374  c.def(TfPyBoolBuiltinFuncName, _IsPtrValid<UnwrappedPtrType>,
375  (char const *)
376  "True if this object has not expired. False otherwise.");
377  c.def("__eq__", _ArePtrsEqual<UnwrappedPtrType>,
378  "Equality operator: x == y");
379  c.def("__ne__", _ArePtrsNotEqual<UnwrappedPtrType>,
380  "Non-equality operator: x != y");
381  c.def( TfTypePythonClass() );
382  }
383 
384  template <typename CLS>
385  void visit(CLS &c) const {
386  typedef typename CLS::wrapped_type Type;
387  typedef typename CLS::metadata::held_type_arg PtrType;
388  static_assert(TF_SUPPORTS_WEAKPTR(Type),
389  "Type must support TfWeakPtr.");
390  // Register conversions
391  _RegisterConversions<PtrType>
392  ((Type *)0, detail::unwrap_wrapper((Type *)0));
393 
394  // Register a PyObjectFinder.
395  Tf_RegisterPythonObjectFinder<Type, PtrType>();
396 
397  // Add weak ptr api.
398  _AddAPI<PtrType>(c, (Type *)0, detail::unwrap_wrapper((Type *)0));
399  }
400 };
401 
402 struct RefAndWeakPtr : def_visitor<RefAndWeakPtr> {
403  friend class def_visitor_access;
404 
405  template <typename CLS, typename Wrapper, typename T>
406  static void _AddAPI(Wrapper *, T *) {
407  _PtrFromPython<TfRefPtr<T> >();
408  typedef typename
409  _PtrInterface<typename CLS::metadata::held_type>::template
410  Rebind<T>::Type PtrType;
411  _ConvertPtrToPython<TfRefPtr<T>, PtrType>();
412  }
413 
414  template <typename CLS>
415  void visit(CLS &c) const {
416  typedef typename CLS::wrapped_type Type;
417  static_assert(TF_SUPPORTS_REFPTR(Type),
418  "Type must support TfRefPtr.");
419  // Same as weak ptr plus ref conversions.
420  WeakPtr().visit(c);
421  _AddAPI<CLS>((Type *)0, detail::unwrap_wrapper((Type *)0));
422  }
423 };
424 
425 };
426 
427 struct TfPyWeakPtr : Tf_PyDefHelpers::WeakPtr {};
428 struct TfPyRefAndWeakPtr : Tf_PyDefHelpers::RefAndWeakPtr {};
429 
430 PXR_NAMESPACE_CLOSE_SCOPE
431 
432 #endif // PXR_BASE_TF_PY_PTR_HELPERS_H
Compatibility code for supporting python 2 and 3.
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