Library of Assembled Shared Sources
 
Loading...
Searching...
No Matches
pyobject_ptr.h
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2025 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43#ifndef LASS_GUARDIAN_OF_INCLUSION_PYTHON_PYOBJECT_PTR_H
44#define LASS_GUARDIAN_OF_INCLUSION_PYTHON_PYOBJECT_PTR_H
45
46#include "python_common.h"
47#include "gil.h"
48#include "../util/shared_ptr.h"
49#include "../util/singleton.h"
50#include "../util/thread.h"
51
52namespace lass
53{
54namespace python
55{
56
57class PyObjectPlus;
58
59namespace impl
60{
61 /** @internal
62 * Fast reentrant lock for reference count operations
63 */
64 inline util::CriticalSection& referenceMutex()
65 {
66 using namespace util;
67 return *Singleton<CriticalSection, destructionPriorityInternalPythonMutex>::instance();
68 }
69
70 template <typename PyObjectPtr>
71 void forceObjectType(PyObjectPtr object, PyTypeObject* type)
72 {
73 if (object && object->ob_type != LASS_ENFORCE_POINTER(type))
74 {
75 LockGIL lock;
76 Py_XINCREF(type);
77 std::swap(object->ob_type, type);
78 Py_XDECREF(type);
79 }
80 }
81
82 LASS_PYTHON_DLL void LASS_CALL doFixObjectType(PyObjectPlus* object);
83 inline void doFixObjectType(const PyObjectPlus* object) { doFixObjectType(const_cast<PyObjectPlus*>(object)); }
84 inline void doFixObjectType(const PyObject*) {}
85
86 /** @internal
87 * On creation, PyObjectPlus are typeless (ob_type == 0),
88 * call this function to fix that for you.
89 * Objects created in C++ should call this at least once before
90 * being used in Python.
91 */
92 template <typename T> T* fixObjectType(T* object)
93 {
94 doFixObjectType(object);
95 return object;
96 }
97}
98
99
100
101/** Recommended storage policy for single PyObject objects, implementation of StoragePolicy concept
102* @ingroup Python
103* @author Tom De Muer [TDM]
104* @see ObjectStorage
105*/
106template <typename T, typename Cascade = meta::EmptyType>
107class PyObjectStorage: public Cascade
108{
109public:
110
111 typedef PyObjectStorage<T, Cascade> TSelf;
112 typedef T* TStorage;
113 typedef T* TPointer;
114 typedef T& TReference;
115
116 TStorage& storage() { return storage_; }
117 const TStorage& storage() const { return storage_; }
118
119protected:
120
121 PyObjectStorage(): Cascade(), storage_(defaultStorage()) {}
122 explicit PyObjectStorage(T* pointee): Cascade(), storage_(impl::fixObjectType(pointee)) {}
123 PyObjectStorage(const PyObjectStorage& other): Cascade(other), storage_(other.storage_) {}
124 PyObjectStorage(PyObjectStorage&& other) noexcept:
125 Cascade(std::forward<Cascade>(other)),
126 storage_(other.storage_)
127 {
128 other.storage_ = nullptr;
129 }
130 template <typename U> PyObjectStorage(const PyObjectStorage<U, Cascade>& other):
131 Cascade(other), storage_(other.storage()) {}
132 TPointer pointer() const { return storage_; }
133 void dispose() { storage_ = 0; }
134 bool isNull() const { return !storage_; }
135 void swap(TSelf& other) { Cascade::swap(other); std::swap(storage_, other.storage_); }
136 static TStorage defaultStorage() { return 0; }
137 template <typename U> const PyObjectStorage<U, Cascade> staticCast() const
138 {
139 return PyObjectStorage<U, Cascade>(static_cast<U*>(storage_), *this);
140 }
141 template <typename U> const PyObjectStorage<U, Cascade> dynamicCast() const
142 {
143 if (U* p = dynamic_cast<U*>(storage_))
144 {
145 return PyObjectStorage<U, Cascade>(p, *this);
146 }
147 return PyObjectStorage<U, Cascade>();
148 }
149 template <typename U> const PyObjectStorage<U, Cascade> constCast() const
150 {
151 return PyObjectStorage<U, Cascade>(const_cast<U*>(storage_), *this);
152 }
153private:
154 template <typename U, typename C> friend class PyObjectStorage;
155 PyObjectStorage(T* pointee, const Cascade& cascade): Cascade(cascade), storage_(pointee) {}
156 TStorage storage_;
157};
158
159
160
161/** The recommended counter for the pyobject pointers, implementation of CounterPolicy concept.
162* @ingroup Python
163* @author Tom De Muer [TDM]
164* @see DefaultCounter
165*/
166class LASS_PYTHON_DLL PyObjectCounter
167{
168public:
169 typedef int TCount;
170protected:
171 PyObjectCounter() {}
172 template <typename T> void init(T* /*pointee*/) {}
173 template <typename T> void dispose(T* /*pointee*/) {}
174 template <typename T> void increment(T* pointee)
175 {
176 if (Py_IsInitialized())
177 {
178 LockGIL lock;
179 Py_INCREF(pointee);
180 }
181 else
182 {
183 Py_INCREF(pointee);
184 }
185 }
186 template <typename T> void increment(const T* pointee)
187 {
188 increment(const_cast<T*>(pointee));
189 }
190 template <typename T> bool decrement(T* pointee)
191 {
192 LASS_ASSERT(pointee);
193 bool r = false;
194 if (Py_IsInitialized())
195 {
196 LockGIL lock;
197#ifdef Py_GIL_DISABLED
198 r = PyUnstable_Object_IsUniquelyReferenced(pointee);
199#else
200 r = Py_REFCNT(pointee) <=1;
201#endif
202 Py_DECREF(pointee);
203 }
204 else
205 {
206#ifdef Py_GIL_DISABLED
207 // there's nothing we can do here anymore, we'll have to leak the object ...
208#else
209 r = pointee->ob_refcnt <=1;
210 if (--pointee->ob_refcnt == 0)
211 {
212 if constexpr (std::is_convertible_v<T*, PyObjectPlus*>)
213 {
214 delete pointee;
215 }
216 else
217 {
218 // fingers crossed!
219 PyTypeObject* type = Py_TYPE(pointee);
220 destructor dealloc = type->tp_dealloc;
221 (*dealloc)(pointee);
222 }
223 }
224#endif
225 }
226 return r;
227 }
228 template <typename T> bool decrement(const T* pointee)
229 {
230 return decrement(const_cast<T*>(pointee));
231 }
232 template <typename T> TCount count(T* pointee) const
233 {
234 LASS_ASSERT(pointee);
235 return Py_REFCNT(pointee);
236 }
237 void swap(PyObjectCounter& /*other*/) {}
238};
239
240
241
242/** templated "typedef" to a python shared pointer
243 * @ingroup Python
244 */
245template<class T>
247{
248 typedef util::SharedPtr<T, PyObjectStorage, PyObjectCounter> Type;
249};
250
251
252
253/** PyObjectPtr to a PyObject
254 * @ingroup Python
255 *
256 * Due to historical reasons, the names PyObjectPtr and TPyObjPtr are a bit
257 * confusing: TPyObjPtr is a PyObjectPtr for PyObject, while PyObjectPtr
258 * is the general one.
259 */
260typedef PyObjectPtr<PyObject>::Type TPyObjPtr;
261
262
263
264/** fromNakedToSharedPtrCast.
265* @ingroup Python
266* Helper function casting a PyObject coming from the Python interface to a SharedPtr
267* object for use in C++. Reference counts are taken care of (it's icremented by one).
268*/
269template<class T>
270lass::util::SharedPtr<T, PyObjectStorage, PyObjectCounter>
272{
273 LockGIL lock;
274 Py_XINCREF(object);
275 return util::SharedPtr<T,PyObjectStorage,PyObjectCounter>(static_cast<T*>(object));
276}
277
278
279
280/** fromSharedPtrToNakedCast.
281* @ingroup Python
282* Helper function casting an object used in C++ for use in Python. The key operation
283* done here is to take care of the reference counting. Failing to use this function may
284* yield unexpected reference count.
285*/
286template<class T>
287PyObject*
288fromSharedPtrToNakedCast(const util::SharedPtr<T,PyObjectStorage,PyObjectCounter>& object)
289{
290 LockGIL lock;
291 PyObject* const obj = object.get();
292 Py_XINCREF(obj);
293 return obj;
294}
295
296
297
298/** static_cast for python pointers
299 * @ingroup Python
300 */
301template <typename Out, typename In> inline
302Out staticPyCast(const In& in)
303{
304 typedef typename Out::TPointer TPtr;
305 LockGIL lock;
306 TPtr ptr = static_cast<TPtr>(in.get());
307 Py_XINCREF(ptr);
308 return Out(ptr);
309}
310
311
312
313/** dynamic_cast for python pointers
314 * @ingroup Python
315 */
316template <typename Out, typename In> inline
317Out dynamicPyCast(const In& in)
318{
319 typedef typename Out::TPointer TPtr;
320 LockGIL lock;
321 TPtr ptr = dynamic_cast<TPtr>(in.get());
322 Py_XINCREF(ptr);
323 return Out(ptr);
324}
325
326}
327}
328
329#endif
330
331// EOF
acquire the GIL for the current scope.
Definition gil.h:56
PyObjectPtr< PyObject >::Type TPyObjPtr
PyObjectPtr to a PyObject.
Out staticPyCast(const In &in)
static_cast for python pointers
PyObject * fromSharedPtrToNakedCast(const util::SharedPtr< T, PyObjectStorage, PyObjectCounter > &object)
fromSharedPtrToNakedCast.
Out dynamicPyCast(const In &in)
dynamic_cast for python pointers
lass::util::SharedPtr< T, PyObjectStorage, PyObjectCounter > fromNakedToSharedPtrCast(PyObject *object)
fromNakedToSharedPtrCast.
Comprehensive C++ to Python binding library.
Library for Assembled Shared Sources.
Definition config.h:53
templated "typedef" to a python shared pointer