Library of Assembled Shared Sources
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 LASS_UNUSED(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 LASS_UNUSED(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 LASS_UNUSED(lock);
197 r = pointee->ob_refcnt <=1;
198 Py_DECREF(pointee);
199 }
200 else
201 {
202 r = pointee->ob_refcnt <=1;
203 if (--pointee->ob_refcnt == 0)
204 {
205 if constexpr (std::is_convertible_v<T*, PyObjectPlus*>)
206 {
207 delete pointee;
208 }
209 else
210 {
211 // fingers crossed!
212 PyTypeObject* type = Py_TYPE(pointee);
213 destructor dealloc = type->tp_dealloc;
214 (*dealloc)(pointee);
215 }
216 }
217 }
218 return r;
219 }
220 template <typename T> bool decrement(const T* pointee)
221 {
222 return decrement(const_cast<T*>(pointee));
223 }
224 template <typename T> TCount count(T* pointee) const
225 {
226 LASS_ASSERT(pointee);
227 return pointee->ob_refcnt;
228 }
229 void swap(PyObjectCounter& /*other*/) {}
230};
231
232
233
234/** templated "typedef" to a python shared pointer
235 * @ingroup Python
236 */
237template<class T>
239{
240 typedef util::SharedPtr<T, PyObjectStorage, PyObjectCounter> Type;
241};
242
243
244
245/** PyObjectPtr to a PyObject
246 * @ingroup Python
247 *
248 * Due to historical reasons, the names PyObjectPtr and TPyObjPtr are a bit
249 * confusing: TPyObjPtr is a PyObjectPtr for PyObject, while PyObjectPtr
250 * is the general one.
251 */
252typedef PyObjectPtr<PyObject>::Type TPyObjPtr;
253
254
255
256/** fromNakedToSharedPtrCast.
257* @ingroup Python
258* Helper function casting a PyObject coming from the Python interface to a SharedPtr
259* object for use in C++. Reference counts are taken care of (it's icremented by one).
260*/
261template<class T>
262lass::util::SharedPtr<T, PyObjectStorage, PyObjectCounter>
264{
265 LockGIL LASS_UNUSED(lock);
266 Py_XINCREF(object);
267 return util::SharedPtr<T,PyObjectStorage,PyObjectCounter>(static_cast<T*>(object));
268}
269
270
271
272/** fromSharedPtrToNakedCast.
273* @ingroup Python
274* Helper function casting an object used in C++ for use in Python. The key operation
275* done here is to take care of the reference counting. Failing to use this function may
276* yield unexpected reference count.
277*/
278template<class T>
279PyObject*
280fromSharedPtrToNakedCast(const util::SharedPtr<T,PyObjectStorage,PyObjectCounter>& object)
281{
282 LockGIL LASS_UNUSED(lock);
283 PyObject* const obj = object.get();
284 Py_XINCREF(obj);
285 return obj;
286}
287
288
289
290/** static_cast for python pointers
291 * @ingroup Python
292 */
293template <typename Out, typename In> inline
294Out staticPyCast(const In& in)
295{
296 typedef typename Out::TPointer TPtr;
297 LockGIL LASS_UNUSED(lock);
298 TPtr ptr = static_cast<TPtr>(in.get());
299 Py_XINCREF(ptr);
300 return Out(ptr);
301}
302
303
304
305/** dynamic_cast for python pointers
306 * @ingroup Python
307 */
308template <typename Out, typename In> inline
309Out dynamicPyCast(const In& in)
310{
311 typedef typename Out::TPointer TPtr;
312 LockGIL LASS_UNUSED(lock);
313 TPtr ptr = dynamic_cast<TPtr>(in.get());
314 Py_XINCREF(ptr);
315 return Out(ptr);
316}
317
318}
319}
320
321#endif
322
323// 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