All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
pyIdentity.h
1//
2// Copyright 2016 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_BASE_TF_PY_IDENTITY_H
8#define PXR_BASE_TF_PY_IDENTITY_H
9
10#include "pxr/pxr.h"
11
12#include "pxr/base/tf/api.h"
13#include "pxr/base/tf/pyLock.h"
14#include "pxr/base/tf/pyUtils.h"
15
18#include "pxr/base/tf/refPtr.h"
21#include "pxr/base/tf/weakPtr.h"
22
23#include "pxr/external/boost/python/handle.hpp"
24
25#include "pxr/base/tf/hashmap.h"
26
27// Specializations for pxr_boost::python::pointee and get_pointer for TfRefPtr and
28// TfWeakPtr.
29namespace PXR_BOOST_NAMESPACE { namespace python {
30
31// TfWeakPtrFacade
32template <template <class> class X, class Y>
33struct pointee< PXR_NS::TfWeakPtrFacade<X, Y> > {
34 typedef Y type;
35};
36
37// TfRefPtr
38template <typename T>
39struct pointee< PXR_NS::TfRefPtr<T> > {
40 typedef T type;
41};
42
43}}
44
45PXR_NAMESPACE_OPEN_SCOPE
46
47struct Tf_PyIdentityHelper
48{
49 // Set the identity of ptr (which derives from TfPtrBase) to be the
50 // python object \a obj.
51 TF_API
52 static void Set(void const *id, PyObject *obj);
53
54 // Return a new reference to the python object associated with ptr. If
55 // there is none, return 0.
56 TF_API
57 static PyObject *Get(void const *id);
58
59 TF_API
60 static void Erase(void const *id);
61
62 // Acquire a reference to the python object associated with ptrBase
63 // if not already acquired.
64 TF_API
65 static void Acquire(void const *id);
66
67 // Release a reference to the python object associated with ptrBase
68 // if we own a reference.
69 TF_API
70 static void Release(void const *id);
71
72};
73
74template <class Ptr>
75void Tf_PyReleasePythonIdentity(Ptr const &ptr, PyObject *obj)
76{
77 Tf_PySetPythonIdentity(ptr, obj);
78 Tf_PyIdentityHelper::Release(ptr.GetUniqueIdentifier());
79}
80
81void Tf_PyOwnershipRefBaseUniqueChanged(TfRefBase const *refBase,
82 bool isNowUnique);
83
84struct Tf_PyOwnershipPtrMap
85{
86 typedef TfHashMap<TfRefBase const *, void const *, TfHash>
87 _CacheType;
88 TF_API
89 static void Insert(TfRefBase *refBase, void const *uniqueId);
90 TF_API
91 static void const *Lookup(TfRefBase const *refBase);
92 TF_API
93 static void Erase(TfRefBase *refBase);
94 private:
95 static _CacheType _cache;
96};
97
98
99// Doxygen generates files whose names are mangled typenames. This is fine
100// except when the filenames get longer than 256 characters. This is one case
101// of that, so we'll just disable doxygen. There's no actual doxygen doc here,
102// so this is fine. If/when this gets solved for real, we can remove this
103// (6/06)
104#ifndef doxygen
105
106
107template <class Ptr, typename Enable = void>
108struct Tf_PyOwnershipHelper {
109 template <typename U>
110 static void Add(U const &, const void *, PyObject *) {}
111 template <typename U>
112 static void Remove(U const &, PyObject *) {}
113};
114
115template <typename Ptr>
116struct Tf_PyOwnershipHelper<Ptr,
117 std::enable_if_t<
118 std::is_same<TfRefPtr<typename Ptr::DataType>, Ptr>::value &&
119 std::is_base_of<TfRefBase, typename Ptr::DataType>::value>>
120{
121 static void Add(Ptr ptr, const void *uniqueId, PyObject *self) {
122
123 TfPyLock pyLock;
124
125 // Create a capsule to hold on to a heap-allocated instance of
126 // Ptr. We'll set this as an attribute on the Python object so
127 // it keeps the C++ object alive.
128 pxr_boost::python::handle<> capsule(
129 PyCapsule_New(
130 new Ptr(ptr), "refptr",
131 +[](PyObject* capsule) {
132 void* heldPtr = PyCapsule_GetPointer(capsule, "refptr");
133 delete static_cast<Ptr*>(heldPtr);
134 }));
135
136 int ret = PyObject_SetAttrString(self, "__owner", capsule.get());
137 if (ret == -1) {
138 // CODE_COVERAGE_OFF
139 TF_WARN("Could not set __owner attribute on python object!");
140 PyErr_Clear();
141 return;
142 // CODE_COVERAGE_ON
143 }
144 TfRefBase *refBase =
145 static_cast<TfRefBase *>(get_pointer(ptr));
146 Tf_PyOwnershipPtrMap::Insert(refBase, uniqueId);
147 }
148
149 static void Remove(Ptr ptr, PyObject *obj) {
150 TfPyLock pyLock;
151
152 if (!ptr) {
153 // CODE_COVERAGE_OFF Can only happen if there's a bug.
154 TF_CODING_ERROR("Removing ownership from null/expired ptr!");
155 return;
156 // CODE_COVERAGE_ON
157 }
158
159 if (PyObject_HasAttrString(obj, "__owner")) {
160 // We are guaranteed that ptr is not unique at this point,
161 // as __owner has a reference and ptr is itself a
162 // reference. This also guarantees us that the object owns
163 // a reference to its python object, so we don't need to
164 // explicitly acquire a reference here.
165 TF_AXIOM(!ptr->IsUnique());
166 // Remove this object from the cache of refbase to uniqueId
167 // that we use for python-owned things.
168 Tf_PyOwnershipPtrMap::Erase(get_pointer(ptr));
169 // Remove the __owner attribute.
170 if (PyObject_DelAttrString(obj, "__owner") == -1) {
171 // CODE_COVERAGE_OFF It's hard to make this occur.
172 TF_WARN("Undeletable __owner attribute on python object!");
173 PyErr_Clear();
174 // CODE_COVERAGE_ON
175 }
176 }
177 }
178};
179
180#endif // doxygen -- see comment above.
181
182
183template <typename Ptr>
184struct Tf_PyIsRefPtr {
185 static const bool value = false;
186};
187
188template <typename T>
189struct Tf_PyIsRefPtr<TfRefPtr<T> > {
190 static const bool value = true;
191};
192
193
194template <class Ptr>
195std::enable_if_t<Tf_PyIsRefPtr<Ptr>::value>
196Tf_PySetPythonIdentity(Ptr const &, PyObject *)
197{
198}
199
200template <class Ptr>
201std::enable_if_t<!Tf_PyIsRefPtr<Ptr>::value>
202Tf_PySetPythonIdentity(Ptr const &ptr, PyObject *obj)
203{
204 if (ptr.GetUniqueIdentifier()) {
205 Tf_PyIdentityHelper::Set(ptr.GetUniqueIdentifier(), obj);
206 // Make sure we hear about it when this weak base dies so we can remove
207 // it from the map.
208 ptr.EnableExtraNotification();
209 }
210}
211
212template <class Ptr>
213PyObject *Tf_PyGetPythonIdentity(Ptr const &ptr)
214{
215 PyObject *ret = Tf_PyIdentityHelper::Get(ptr.GetUniqueIdentifier());
216 return ret;
217}
218
219template <class Ptr>
220void Tf_PyRemovePythonOwnership(Ptr const &t, PyObject *obj)
221{
222 Tf_PyOwnershipHelper<Ptr>::Remove(t, obj);
223}
224
225template <class Ptr>
226void Tf_PyAddPythonOwnership(Ptr const &t, const void *uniqueId, PyObject *obj)
227{
228 Tf_PyOwnershipHelper<Ptr>::Add(t, uniqueId, obj);
229}
230
231PXR_NAMESPACE_CLOSE_SCOPE
232
233#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.
Convenience class for accessing the Python Global Interpreter Lock.
Definition: pyLock.h:105
Enable a concrete base class for use with TfRefPtr.
Definition: refBase.h:56
Reference-counted smart pointer utility class.
Definition: refPtr.h:590
Demangle C++ typenames generated by the typeid() facility.
#define TF_AXIOM(cond)
Aborts if the condition cond is not met.
Definition: diagnostic.h:193
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:68
#define TF_WARN(...)
Issue a warning, but continue execution.
Definition: diagnostic.h:132
STL namespace.
Reference counting.
Safely compare C++ RTTI type structures.
Definitions of basic string utilities in tf.
Pointer storage with deletion detection.