All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyIdentity.h
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_PYIDENTITY_H
25 #define TF_PYIDENTITY_H
26 
27 #include "pxr/pxr.h"
28 
29 #include "pxr/base/tf/api.h"
30 #include "pxr/base/tf/pyLock.h"
31 #include "pxr/base/tf/pyUtils.h"
32 
33 #include "pxr/base/arch/demangle.h"
34 #include "pxr/base/tf/diagnostic.h"
35 #include "pxr/base/tf/refPtr.h"
36 #include "pxr/base/tf/safeTypeCompare.h"
37 #include "pxr/base/tf/stringUtils.h"
38 #include "pxr/base/tf/weakPtr.h"
39 
40 #include <boost/python/class.hpp>
41 #include <boost/python/handle.hpp>
42 #include <boost/python/object.hpp>
43 #include <boost/type_traits/is_base_of.hpp>
44 #include <boost/utility.hpp>
45 
46 #include "pxr/base/tf/hashmap.h"
47 
48 // Specializations for boost::python::pointee and get_pointer for TfRefPtr and
49 // TfWeakPtr.
50 namespace boost { namespace python {
51 
52 // TfWeakPtrFacade
53 template <template <class> class X, class Y>
54 struct pointee< PXR_NS::TfWeakPtrFacade<X, Y> > {
55  typedef Y type;
56 };
57 
58 // TfRefPtr
59 template <typename T>
60 struct pointee< PXR_NS::TfRefPtr<T> > {
61  typedef T type;
62 };
63 
64 }}
65 
66 PXR_NAMESPACE_OPEN_SCOPE
67 
68 struct Tf_PyIdentityHelper
69 {
70  // Set the identity of ptr (which derives from TfPtrBase) to be the
71  // python object \a obj.
72  TF_API
73  static void Set(void const *id, PyObject *obj);
74 
75  // Return a new reference to the python object associated with ptr. If
76  // there is none, return 0.
77  TF_API
78  static PyObject *Get(void const *id);
79 
80  TF_API
81  static void Erase(void const *id);
82 
83  // Acquire a reference to the python object associated with ptrBase
84  // if not already acquired.
85  TF_API
86  static void Acquire(void const *id);
87 
88  // Release a reference to the python object associated with ptrBase
89  // if we own a reference.
90  TF_API
91  static void Release(void const *id);
92 
93 };
94 
95 template <class Ptr>
96 void Tf_PyReleasePythonIdentity(Ptr const &ptr, PyObject *obj)
97 {
98  Tf_PySetPythonIdentity(ptr, obj);
99  Tf_PyIdentityHelper::Release(ptr.GetUniqueIdentifier());
100 }
101 
102 void Tf_PyOwnershipRefBaseUniqueChanged(TfRefBase const *refBase,
103  bool isNowUnique);
104 
105 struct Tf_PyOwnershipPtrMap
106 {
107  typedef TfHashMap<TfRefBase const *, void const *, TfHash>
108  _CacheType;
109  TF_API
110  static void Insert(TfRefBase *refBase, void const *uniqueId);
111  TF_API
112  static void const *Lookup(TfRefBase const *refBase);
113  TF_API
114  static void Erase(TfRefBase *refBase);
115  private:
116  static _CacheType _cache;
117 };
118 
119 
120 // Doxygen generates files whose names are mangled typenames. This is fine
121 // except when the filenames get longer than 256 characters. This is one case
122 // of that, so we'll just disable doxygen. There's no actual doxygen doc here,
123 // so this is fine. If/when this gets solved for real, we can remove this
124 // (6/06)
125 #ifndef doxygen
126 
127 
128 template <class Ptr, typename Enable = void>
129 struct Tf_PyOwnershipHelper {
130  template <typename U>
131  static void Add(U const &, const void *, PyObject *) {}
132  template <typename U>
133  static void Remove(U const &, PyObject *) {}
134 };
135 
136 template <typename Ptr>
137 struct Tf_PyOwnershipHelper<Ptr,
138  typename boost::enable_if<
139  boost::mpl::and_<boost::is_same<TfRefPtr<typename Ptr::DataType>, Ptr>,
140  boost::is_base_of<TfRefBase, typename Ptr::DataType> > >::type>
141 {
142  struct _RefPtrHolder {
143  static boost::python::object
144  Get(Ptr const &refptr) {
145  TfPyLock pyLock;
146  _WrapIfNecessary();
147  return boost::python::object(_RefPtrHolder(refptr));
148  }
149  static void _WrapIfNecessary() {
150  TfPyLock pyLock;
151  if (TfPyIsNone(TfPyGetClassObject<_RefPtrHolder>())) {
152  std::string name =
153  "__" + ArchGetDemangled(typeid(typename Ptr::DataType)) +
154  "__RefPtrHolder";
155  name = TfStringReplace(name, "<", "_");
156  name = TfStringReplace(name, ">", "_");
157  name = TfStringReplace(name, "::", "_");
158  boost::python::class_<_RefPtrHolder>(name.c_str(),
159  boost::python::no_init);
160  }
161  }
162  private:
163  explicit _RefPtrHolder(Ptr const &refptr) : _refptr(refptr) {}
164  Ptr _refptr;
165  };
166 
167  static void Add(Ptr ptr, const void *uniqueId, PyObject *self) {
168 
169  TfPyLock pyLock;
170 
171  // Make the python object keep the c++ object alive.
172  int ret = PyObject_SetAttrString(self, "__owner",
173  _RefPtrHolder::Get(ptr).ptr());
174  if (ret == -1) {
175  // CODE_COVERAGE_OFF
176  TF_WARN("Could not set __owner attribute on python object!");
177  PyErr_Clear();
178  return;
179  // CODE_COVERAGE_ON
180  }
181  TfRefBase *refBase =
182  static_cast<TfRefBase *>(get_pointer(ptr));
183  Tf_PyOwnershipPtrMap::Insert(refBase, uniqueId);
184  }
185 
186  static void Remove(Ptr ptr, PyObject *obj) {
187  TfPyLock pyLock;
188 
189  if (!ptr) {
190  // CODE_COVERAGE_OFF Can only happen if there's a bug.
191  TF_CODING_ERROR("Removing ownership from null/expired ptr!");
192  return;
193  // CODE_COVERAGE_ON
194  }
195 
196  if (PyObject_HasAttrString(obj, "__owner")) {
197  // We are guaranteed that ptr is not unique at this point,
198  // as __owner has a reference and ptr is itself a
199  // reference. This also guarantees us that the object owns
200  // a reference to its python object, so we don't need to
201  // explicitly acquire a reference here.
202  TF_AXIOM(!ptr->IsUnique());
203  // Remove this object from the cache of refbase to uniqueId
204  // that we use for python-owned things.
205  Tf_PyOwnershipPtrMap::Erase(get_pointer(ptr));
206  // Remove the __owner attribute.
207  if (PyObject_DelAttrString(obj, "__owner") == -1) {
208  // CODE_COVERAGE_OFF It's hard to make this occur.
209  TF_WARN("Undeletable __owner attribute on python object!");
210  PyErr_Clear();
211  // CODE_COVERAGE_ON
212  }
213  }
214  }
215 };
216 
217 #endif // doxygen -- see comment above.
218 
219 
220 template <typename Ptr>
221 struct Tf_PyIsRefPtr {
222  static const bool value = false;
223 };
224 
225 template <typename T>
226 struct Tf_PyIsRefPtr<TfRefPtr<T> > {
227  static const bool value = true;
228 };
229 
230 
231 template <class Ptr>
232 typename boost::enable_if<Tf_PyIsRefPtr<Ptr> >::type
233 Tf_PySetPythonIdentity(Ptr const &, PyObject *)
234 {
235 }
236 
237 template <class Ptr>
238 typename boost::disable_if<Tf_PyIsRefPtr<Ptr> >::type
239 Tf_PySetPythonIdentity(Ptr const &ptr, PyObject *obj)
240 {
241  if (ptr.GetUniqueIdentifier()) {
242  Tf_PyIdentityHelper::Set(ptr.GetUniqueIdentifier(), obj);
243  // Make sure we hear about it when this weak base dies so we can remove
244  // it from the map.
245  ptr.EnableExtraNotification();
246  }
247 }
248 
249 template <class Ptr>
250 PyObject *Tf_PyGetPythonIdentity(Ptr const &ptr)
251 {
252  PyObject *ret = Tf_PyIdentityHelper::Get(ptr.GetUniqueIdentifier());
253  return ret;
254 }
255 
256 template <class Ptr>
257 void Tf_PyRemovePythonOwnership(Ptr const &t, PyObject *obj)
258 {
259  Tf_PyOwnershipHelper<Ptr>::Remove(t, obj);
260 }
261 
262 template <class Ptr>
263 void Tf_PyAddPythonOwnership(Ptr const &t, const void *uniqueId, PyObject *obj)
264 {
265  Tf_PyOwnershipHelper<Ptr>::Add(t, uniqueId, obj);
266 }
267 
268 PXR_NAMESPACE_CLOSE_SCOPE
269 
270 #endif // TF_PYIDENTITY_H
#define TF_WARN(...)
Issue a warning, but continue execution.
Definition: diagnostic.h:153
#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.
Enable a concrete base class for use with TfRefPtr.
Definition: refBase.h:72
#define TF_AXIOM(cond)
Aborts if the condition cond is not met.
Definition: diagnostic.h:215
TF_API bool TfPyIsNone(boost::python::object const &obj)
Return true iff obj is None.
Reference-counted smart pointer utility class.
Definition: hash.h:45
TF_API std::string TfStringReplace(const std::string &source, const std::string &from, const std::string &to)
Replaces all occurrences of string from with to in source.