All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyWeakObject.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_PYWEAKOBJECT_H
25 #define TF_PYWEAKOBJECT_H
26 
27 #include "pxr/pxr.h"
28 
29 #include "pxr/base/tf/api.h"
30 #include "pxr/base/tf/pyIdentity.h"
31 
32 #include "pxr/base/tf/hash.h"
33 #include "pxr/base/tf/singleton.h"
34 #include "pxr/base/tf/weakBase.h"
35 #include "pxr/base/tf/weakPtr.h"
36 
37 #include <boost/python/class.hpp>
38 #include <boost/python/handle.hpp>
39 #include <boost/python/object.hpp>
40 
41 #include "pxr/base/tf/hashmap.h"
42 
43 PXR_NAMESPACE_OPEN_SCOPE
44 
46 
47 struct Tf_PyWeakObjectRegistry
48 {
49  typedef Tf_PyWeakObjectRegistry This;
50 
52  TF_API static This &GetInstance() {
54  }
55 
56  void Insert(PyObject *obj, Tf_PyWeakObjectPtr const &weakObj) {
57  _weakObjects[obj] = weakObj;
58  }
59 
60  Tf_PyWeakObjectPtr Lookup(PyObject *obj) {
61  if (_weakObjects.count(obj))
62  return _weakObjects[obj];
63  return Tf_PyWeakObjectPtr();
64  }
65 
66  void Remove(PyObject *obj) {
67  _weakObjects.erase(obj);
68  }
69 
70  private:
71 
72  Tf_PyWeakObjectRegistry() {}
73  virtual ~Tf_PyWeakObjectRegistry();
74  friend class TfSingleton<This>;
75 
76  TfHashMap<PyObject *, Tf_PyWeakObjectPtr, TfHash> _weakObjects;
77 
78 };
79 
80 TF_API_TEMPLATE_CLASS(TfSingleton<Tf_PyWeakObjectRegistry>);
81 
82 // A weak pointable weak reference to a python object.
83 struct Tf_PyWeakObject : public TfWeakBase
84 {
85 public:
86  typedef Tf_PyWeakObject This;
87 
88  // A deleter instance is passed to PyWeakref_NewRef as the callback object
89  // so that when the python object we have the weak ref to dies, we can
90  // delete the corresponding weak object.
91  struct Deleter {
92  static void WrapIfNecessary() {
93  if (TfPyIsNone(TfPyGetClassObject<Deleter>()))
94  boost::python::class_<Deleter>
95  ("Tf_PyWeakObject__Deleter", boost::python::no_init)
96  .def("__call__", &This::Deleter::Deleted);
97  }
98  explicit Deleter(Tf_PyWeakObjectPtr const &self)
99  : _self(self) {
100  WrapIfNecessary();
101  }
102  void Deleted(PyObject * /* weakRef */) {
103  _self->Delete();
104  }
105  private:
106  Tf_PyWeakObjectPtr _self;
107  };
108 
109  static Tf_PyWeakObjectPtr GetOrCreate(boost::python::object const &obj) {
110  // If it's in the registry, return it.
111  if (Tf_PyWeakObjectPtr p =
112  Tf_PyWeakObjectRegistry::GetInstance().Lookup(obj.ptr()))
113  return p;
114  // Otherwise, make sure we can create a python weak reference to the
115  // object.
116  if (PyObject *weakRef = PyWeakref_NewRef(obj.ptr(), NULL)) {
117  Py_DECREF(weakRef);
118  return TfCreateWeakPtr(new Tf_PyWeakObject(obj));
119  }
120  // Cannot create a weak reference to obj -- return a null pointer.
121  PyErr_Clear();
122  return Tf_PyWeakObjectPtr();
123  }
124 
125  boost::python::object GetObject() const {
126  return boost::python::object
127  (boost::python::handle<>
128  (boost::python::borrowed(PyWeakref_GetObject(_weakRef.get()))));
129  }
130 
131  void Delete() {
132  Tf_PyWeakObjectRegistry::GetInstance().Remove(GetObject().ptr());
133  delete this;
134  }
135 
136  private:
137  explicit Tf_PyWeakObject(boost::python::object const &obj) :
138  _weakRef(PyWeakref_NewRef(obj.ptr(), boost::python::
139  object(Deleter(TfCreateWeakPtr(this))).ptr()))
140  {
141  Tf_PyWeakObjectPtr self(this);
142 
143  // Set our python identity, but release it immediately, since we are a
144  // weak reference and will expire as soon as the python object does.
145  Tf_PyReleasePythonIdentity(self, GetObject().ptr());
146 
147  // Install us in the registry.
148  Tf_PyWeakObjectRegistry::GetInstance().Insert(GetObject().ptr(), self);
149  }
150 
151  boost::python::handle<> _weakRef;
152 
153 };
154 
155 PXR_NAMESPACE_CLOSE_SCOPE
156 
157 #endif // TF_PYWEAKOBJECT_H
Manage a single instance of an object (see.
Definition: singleton.h:122
Pointer storage with deletion detection.
Definition: hash.h:44
static T & GetInstance()
Return a reference to an object of type T, creating it if necessary.
Definition: singleton.h:137
TF_API bool TfPyIsNone(boost::python::object const &obj)
Return true iff obj is None.
Enable a concrete base class for use with TfWeakPtr.
Definition: weakBase.h:142