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