Library of Assembled Shared Sources
 
Loading...
Searching...
No Matches
pyshadow_object.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 * https://lass.cocamware.com/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) 2023-2026 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/** @file
44 * @author Bram de Greve (bram@cocamware.com)
45 * @author Tom De Muer (tom@cocamware.com)
46 *
47 * Distributed under the terms of the GPL (GNU Public License)
48 *
49 * The LASS License:
50 *
51 * Copyright 2004-2008 Bram de Greve and Tom De Muer
52 *
53 * LASS is free software; you can redistribute it and/or modify
54 * it under the terms of the GNU General Public License as published by
55 * the Free Software Foundation; either version 2 of the License, or
56 * (at your option) any later version.
57 *
58 * This program is distributed in the hope that it will be useful,
59 * but WITHOUT ANY WARRANTY; without even the implied warranty of
60 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
61 * GNU General Public License for more details.
62 *
63 * You should have received a copy of the GNU General Public License
64 * along with this program; if not, write to the Free Software
65 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
66 *
67 * @note
68 * This header bundles the _EXPERIMENTAL_ code for quasi automatic
69 * wrapping of C++ object hierarchies into python shadow objects. This
70 * code is still heavily under development and not ready for production use.
71 */
72
73#ifndef LASS_GUARDIAN_OF_INCLUSION_UTIL_PYSHADOW_OBJECT_H
74#define LASS_GUARDIAN_OF_INCLUSION_UTIL_PYSHADOW_OBJECT_H
75
76#include "python_common.h"
77#include "pyobject_plus.h"
78#include "shadowee_traits.h"
79#include "../meta/is_derived.h"
80#include <type_traits>
81
82namespace lass
83{
84namespace python
85{
86
87/** @defgroup ShadowClasses Python Shadow Classes
88 * @brief Export C++ classes that don't derive from lass::python::PyObjectPlus
89 * @ingroup Python
90 *
91 * Shadow classes are LASS' wrapper classes that allow you to export C++ classes that don't
92 * derive from lass::python::PyObjectPlus to Python.
93 *
94 * They are defined by invoking two macros:
95 *
96 * - PY_SHADOW_CLASS defines the shadow class. It's typical to name the shadow class by prefixing
97 * the native C++ class with Py. For example, Spam becomes PySpam. This macro can be invoked in
98 * any namespace.
99 *
100 * - PY_SHADOW_CASTERS defines the ShadoweeTraits for the shadow class to make sure you can
101 * translate from the native C++ class to the shadow class and vice-versa. This must be invoked
102 * in the global namespace.
103 *
104 * Both macros are typically invoked in a header file (not necessarily the one that defines the
105 * original C++ classes), as both macros must be seen by all exports that use this class as parameter
106 * or return type.
107 *
108 * Once you have defined the Shadow class, you use the regular macros like PY_DECLARE_CLASS_NAME
109 * or PY_CLASS_METHOD to fully define the Python export for this class. But instead of using the
110 * C++ class as first argument, you use the shadow class.
111 *
112 * Here's a quick comparison between both techniques:
113 *
114 * ```cpp
115 * // Direct Python class | // Shadow Python class
116 * |
117 * // spam.h | // spam.h
118 * |
119 * class Spam: public lass::python::PyObjectPlus | class Spam
120 * { | {
121 * PY_HEADER(lass::python::PyObjectPlus) |
122 * public: | public:
123 * void method(int a, int b); | void method(int a, int b);
124 * }; | };
125 * |
126 * class Ham: public Spam | class Ham: public Spam
127 * { | {
128 * PY_HEADER(Spam) |
129 * public: | public:
130 * void other(int c); | void other(int c);
131 * }; | };
132 * |
133 * | PY_SHADOW_CLASS(LASS_DLL_EXPORT, PySpam, Spam)
134 * | PY_SHADOW_CASTERS(PySpam)
135 * |
136 * | PY_SHADOW_CLASS_DERIVED(LASS_DLL_EXPORT, PyHam, Ham, PySpam)
137 * | PY_SHADOW_CASTERS(PyHam)
138 * |
139 * // spam.cpp | // spam.cpp
140 * |
141 * PY_DECLARE_CLASS(Spam) | PY_DECLARE_CLASS_NAME(PySpam, "Spam")
142 * PY_CLASS_METHOD(Spam, method) | PY_CLASS_METHOD(PySpam, method)
143 * |
144 * PY_DECLARE_CLASS(Ham) | PY_DECLARE_CLASS_NAME(PyHam, "Ham")
145 * PY_CLASS_METHOD(Ham, other) | PY_CLASS_METHOD(PyHam, other)
146 * ```
147 */
148
149
150/** @ingroup ShadowClasses
151 * @brief ID-type to uniquely identify shadowee instances in the shadow cache
152 */
153using TShadoweeID = num::TuintPtr;
154
155namespace impl
156{
157
158/** @ingroup ShadowClasses
159 * @internal
160 */
161enum ShadoweeConstness
162{
163 scConst,
164 scNonConst
165};
166
167/** @ingroup ShadowClasses
168 * @internal
169 */
170class LASS_PYTHON_DLL ShadowBaseCommon: public PyObjectPlus
171{
172public:
173 static TPyObjPtr findShadowObject(TShadoweeID shadoweeID, ShadoweeConstness constness);
174
175protected:
176 ShadowBaseCommon();
177 ~ShadowBaseCommon() override;
178
179 void registerShadowee(TShadoweeID shadoweeID, ShadoweeConstness constness);
180 void unregisterShadowee(TShadoweeID shadoweeID, ShadoweeConstness constness);
181private:
182 typedef std::pair<TShadoweeID, ShadoweeConstness> TCacheKey;
183
184 struct CacheKeyHash
185 {
186 std::size_t operator()(const TCacheKey& key) const
187 {
188 std::size_t h1 = std::hash<TShadoweeID>()(key.first);
189 std::size_t h2 = std::hash<ShadoweeConstness>()(key.second);
190 return h1 ^ h2;
191 }
192 };
193
194 typedef std::unordered_map<TCacheKey, ShadowBaseCommon*, CacheKeyHash> TCache;
195 ShadowBaseCommon(const ShadowBaseCommon&);
196 ShadowBaseCommon& operator=(const ShadowBaseCommon&);
197
198 static TCache& cache();
199};
200
201/** @ingroup ShadowClasses
202 * @internal
203 */
204template <typename T>
205struct IsShadowClass: public meta::IsDerived<T, ShadowBaseCommon>
206{
207};
208
209/** @ingroup ShadowClasses
210 * @internal
211 */
212template <typename T>
213struct ShadowTraits
214{
215 enum { isShadow = IsShadowClass<T>::value };
216
217private:
218
219 template <typename U, bool shadow>
220 struct Impl
221 {
222 typedef typename U::TShadoweePtr TCppClassPtr;
223 typedef typename U::TConstShadoweePtr TConstCppClassPtr;
224 typedef typename U::TShadowPtr TPyClassPtr;
225 typedef typename U::TShadowee TCppClass;
226 static int getObject(U* obj, TCppClassPtr& value)
227 {
228 typedef typename U::TPointerTraits TPointerTraits;
229 const TCppClassPtr p = TPointerTraits::staticCast(obj->cppObject());
230 if (TPointerTraits::isEmpty(p))
231 {
232 PyErr_Format(PyExc_TypeError, "PyObject is a const %s", T::_lassPyClassDef.name());
233 return 1;
234 }
235 value = p;
236 return 0;
237 };
238 static int getObject(U* obj, TConstCppClassPtr& value)
239 {
240 typedef typename U::TConstPointerTraits TConstPointerTraits;
241 value = TConstPointerTraits::staticCast(obj->constCppObject());
242 if (TConstPointerTraits::isEmpty(value))
243 {
244 PyErr_Format(PyExc_TypeError, "Trying to dereference null-PyObject of type %s", T::_lassPyClassDef.name());
245 return 1;
246 }
247 return 0;
248 };
249 template <typename Ptr> static TPyClassPtr buildObject(const Ptr& value)
250 {
251 return U::make(value);
252 }
253 };
254
255 template <typename U>
256 struct Impl<U, false>
257 {
258 typedef typename PyObjectPtr<U>::Type TPyClassPtr;
259 typedef TPyClassPtr TCppClassPtr;
260 typedef typename PyObjectPtr<const U>::Type TConstCppClassPtr;
261 typedef U TCppClass;
262 static int getObject(U* obj, TPyClassPtr& value)
263 {
264 value = fromNakedToSharedPtrCast<U>(obj);
265 return 0;
266 };
267 static int getObject(U* obj, TConstCppClassPtr& value)
268 {
270 return 0;
271 };
272 static const TPyClassPtr& buildObject(const TPyClassPtr& value)
273 {
274 return value;
275 }
276 // we can't create them from const cppObjects, as we can't track it as such ...
277 };
278
279 typedef Impl<T, isShadow> TImpl;
280
281 static bool checkSubType(PyObject* obj)
282 {
283 LASS_ASSERT(obj);
284 if (!PyType_IsSubtype(obj->ob_type , T::_lassPyClassDef.type() ))
285 {
286 PyErr_Format(PyExc_TypeError, "%s not castable to %s", obj->ob_type->tp_name, T::_lassPyClassDef.name());
287 return false;
288 }
289 return true;
290 }
291
292public:
293
294 typedef typename TImpl::TCppClass TCppClass;
295 typedef typename TImpl::TCppClassPtr TCppClassPtr;
296 typedef typename TImpl::TConstCppClassPtr TConstCppClassPtr;
297 typedef typename TImpl::TPyClassPtr TPyClassPtr;
298 typedef int (*TImplicitConverter)(PyObject* obj, TCppClassPtr&);
299
300 template <typename Ptr> static int getObject(PyObject* obj, Ptr& value)
301 {
302 if (obj == Py_None)
303 {
304 value = Ptr();
305 return 0;
306 }
307 if (PyType_IsSubtype(obj->ob_type , T::_lassPyClassDef.type()))
308 {
309 return TImpl::getObject(static_cast<T*>(obj), value);
310 }
311 TCppClassPtr p;
312 if (tryImplicitConverters(obj, p) != 0)
313 {
314 return 1;
315 }
316 value = p;
317 return 0;
318 }
319 static int getObject(PyObject* obj, TCppClass& value)
320 {
321 if (obj == Py_None)
322 {
323 PyErr_Format(PyExc_TypeError, "None not castable to %s", T::_lassPyClassDef.name());
324 return 1;
325 }
326 TConstCppClassPtr p;
327 if (obj->ob_type == T::_lassPyClassDef.type())
328 {
329 if (TImpl::getObject(static_cast<T*>(obj), p) != 0)
330 {
331 return 1;
332 }
333 }
334 else
335 {
336 TCppClassPtr p2;
337 if (tryImplicitConverters(obj, p2) != 0)
338 {
339 return 1;
340 }
341 p = p2;
342 }
343 try
344 {
345 value = *p;
346 }
347 LASS_PYTHON_CATCH_AND_RETURN_EX(1)
348 return 0;
349 }
350 template <typename Ptr> static TPyClassPtr buildObject(const Ptr& value)
351 {
352 return TImpl::buildObject(value);
353 }
354 static TPyClassPtr buildObject(const TCppClass& value)
355 {
356 TCppClassPtr p(new TCppClass(value));
357 return buildObject(p);
358 }
359 template <typename Deleter>
360 static TPyClassPtr buildObject(std::unique_ptr<TCppClass, Deleter>&& value)
361 {
362 return buildObject(TCppClassPtr(std::move(value)));
363 }
364 static void addConverter(TImplicitConverter converter)
365 {
366 TImplicitConverterList* converters = implicitConverters();
367 converters->push_back(converter);
368 }
369
370private:
371 typedef std::vector<TImplicitConverter> TImplicitConverterList;
372 static TImplicitConverterList* implicitConverters_;
373
374 static int tryImplicitConverters(PyObject* obj, TCppClassPtr& p)
375 {
376 const TImplicitConverterList* converters = implicitConverters();
377 if (converters)
378 {
379 for (typename TImplicitConverterList::const_iterator i = converters->begin(); i != converters->end(); ++i)
380 {
381 if ((*i)(obj, p) == 0)
382 {
383 return 0;
384 }
385 PyErr_Clear();
386 }
387 }
388 PyErr_Format(PyExc_TypeError, "%s not convertable to %s", obj->ob_type->tp_name, T::_lassPyClassDef.name());
389 return 1;
390 }
391
392 static TImplicitConverterList* implicitConverters()
393 {
394 void*& slot = T::_lassPyClassDef.implicitConvertersSlot_;
395 if (!slot)
396 {
397 slot = new TImplicitConverterList;
398 }
399 return static_cast<TImplicitConverterList*>(slot);
400 }
401};
402
403//template <typename T> typename ShadowTraits<T>::TImplicitConverterList* ShadowTraits<T>::implicitConverters_ = 0;
404
405/** @ingroup ShadowClasses
406 * @internal
407 */
408template <typename ShadowType, typename DerivedMakers>
409typename ShadowType::TShadowPtr makeShadow(
410 const typename ShadowType::TConstShadoweePtr& shadowee, const DerivedMakers* derivedMakers,
411 impl::ShadoweeConstness constness)
412{
413 typedef typename ShadowType::TShadowPtr TShadowPtr;
414 typedef typename ShadowType::TConstPointerTraits TConstPointerTraits;
415
416 LASS_ASSERT(!TConstPointerTraits::isEmpty(shadowee));
417 if (const auto p = impl::ShadowBaseCommon::findShadowObject(TConstPointerTraits::id(shadowee), constness))
418 {
419 LASS_ASSERT(PyObject_IsInstance(p.get(), reinterpret_cast<PyObject*>(ShadowType::_lassPyClassDef.type())));
420 return p.template staticCast<ShadowType>();
421 }
422 if (derivedMakers)
423 {
424 for (typename DerivedMakers::const_iterator i = derivedMakers->begin(); i != derivedMakers->end(); ++i)
425 {
426 if (const TShadowPtr p = (*i)(shadowee, constness))
427 {
428 return p;
429 }
430 }
431 }
432 return TShadowPtr(impl::fixObjectType(new ShadowType(shadowee, constness)));
433}
434
435/** @ingroup ShadowClasses
436 * @internal
437 */
438template <typename Makers, typename Maker> void registerMaker(Makers*& makers, Maker maker)
439{
440 if (!makers)
441 {
442 makers = new Makers;
443 }
444 makers->push_back(maker);
445}
446
447/** @ingroup ShadowClasses
448 * @internal
449 */
450template <typename DestPyType, typename SourceCppType>
451int defaultConvertor(PyObject* object, typename lass::python::impl::ShadowTraits<DestPyType>::TCppClassPtr& p)
452{
453 typedef typename lass::python::impl::ShadowTraits<DestPyType>::TCppClass TCppClass;
454 typedef typename lass::python::impl::ShadowTraits<DestPyType>::TCppClassPtr TPtr;
455 SourceCppType source;
456 if (pyGetSimpleObject(object, source) != 0)
457 {
458 return 1;
459 }
460 p = TPtr(new TCppClass(source));
461 return 0;
462}
463
464}
465
466
467/** @addtogroup ShadowClasses
468 *
469 * @par Pointer-traits
470 *
471 * Shadow-classes need a pointer type to store their shadowee instances. This is
472 * defined by the pointer traits chosen for the shadow class.
473 *
474 * By default, the SharedPointerTraits is used with util::SharedPtr as shadowee pointer,
475 * but you can specify a custom one using PY_SHADOW_CLASS_PTRTRAITS.
476 *
477 * Available pointer traits are:
478 * - SharedPointerTraits
479 * - NakedPointerTraits
480 * - StdSharedPointerTraits
481 *
482 * To make your custom pointer traits, copy the pattern of these three.
483 *
484 * @note `id` must return an ID that uniquely identifies a shadowee instance for as long
485 * as it is alive and at least one shadow pointer references it (when it's removed from
486 * the shadow cache). Return 0 to opt-out from the shadow cache.
487 */
488
489/** @ingroup ShadowClasses
490 * @brief Pointer-traits for Python Shadow classes that use lass::util::SharedPtr for storage
491 */
492template <typename T, template <typename, typename> class S = util::ObjectStorage, typename C = util::DefaultCounter>
494{
495 /** Pointer to shadowee type */
496 typedef util::SharedPtr<T, S, C> TPtr;
497
498 /** Rebind pointer traits to other shadowee type */
499 template <typename U> struct Rebind
500 {
501 typedef SharedPointerTraits<U, S, C> Type;
502 };
503
504 /** util::SharedPtr already handles reference counts */
505 static void acquire(const TPtr&) {}
506 /** util::SharedPtr already handles reference counts */
507 static void release(const TPtr&) {}
508
509 /** Return true when storing a nullptr */
510 static bool isEmpty(const TPtr& p)
511 {
512 return p.isEmpty();
513 }
514 /** Get the raw pointer to the shadowee */
515 static T* get(const TPtr& p)
516 {
517 return p.get();
518 }
519
520 /** Convert shadowee pointer to ID for shadow cache */
521 static TShadoweeID id(const TPtr& p)
522 {
523 return reinterpret_cast<TShadoweeID>(p.get());
524 }
525
526 /** Perform static cast on shadowee pointer */
527 template <typename U> static TPtr staticCast(const util::SharedPtr<U, S, C>& p)
528 {
529 return p.template staticCast<T>();
530 }
531 /** Perform dynamic cast on shadowee pointer */
532 template <typename U> static TPtr dynamicCast(const util::SharedPtr<U, S, C>& p)
533 {
534 return p.template dynamicCast<T>();
535 }
536 /** Perform const cast on shadowee pointer */
537 template <typename U> static TPtr constCast(const util::SharedPtr<U, S, C>& p)
538 {
539 return p.template constCast<T>();
540 }
541};
542
543
544/** @ingroup ShadowClasses
545 * @brief Pointer-traits for Python Shadow classes that use raw `*` pointers for storage
546 *
547 * @warning The NakedPointerTraits does not govern the lifetime of the shadowee, so you
548 * must make sure that it outlives every Python reference!
549 */
550template <typename T>
552{
553 /** Pointer to shadowee type */
554 typedef T* TPtr;
555
556 /** Rebind pointer traits to other shadowee type */
557 template <typename U> struct Rebind
558 {
559 typedef NakedPointerTraits<U> Type;
560 };
561
562 /** Raw pointers have no ownership rules */
563 static void acquire(TPtr) {}
564 /** Raw pointers have no ownership rules */
565 static void release(TPtr) {}
566
567 /** Return true when storing a nullptr */
568 static bool isEmpty(TPtr p)
569 {
570 return p == 0;
571 }
572 /** Get the raw pointer to the shadowee */
573 static T* get(TPtr p)
574 {
575 return p;
576 }
577
578 /** Convert shadowee pointer to ID for shadow cache */
580 {
581 return reinterpret_cast<TShadoweeID>(p);
582 }
583
584 /** Perform static cast on shadowee pointer */
585 template <typename U> static TPtr staticCast(U* p)
586 {
587 return static_cast<TPtr>(p);
588 }
589 /** Perform dynamic cast on shadowee pointer */
590 template <typename U> static TPtr dynamicCast(U* p)
591 {
592 return dynamic_cast<TPtr>(p);
593 }
594 /** Perform const cast on shadowee pointer */
595 template <typename U> static TPtr constCast(U* p)
596 {
597 return const_cast<TPtr>(p);
598 }
599};
600
601
602/** @ingroup ShadowClasses
603 * @brief Pointer-traits for Python Shadow classes that use `std::shared_ptr` for storage
604 */
605template <typename T>
607{
608 /** Pointer to shadowee type */
609 typedef std::shared_ptr<T> TPtr;
610
611 /** Rebind pointer traits to other shadowee type */
612 template <typename U> struct Rebind
613 {
614 typedef StdSharedPointerTraits<U> Type;
615 };
616
617 /** std::shared_ptr already handles reference counts */
618 static void acquire(const TPtr&) {}
619 /** std::shared_ptr already handles reference counts */
620 static void release(const TPtr&) {}
621
622 /** Return true when storing a nullptr */
623 static bool isEmpty(const TPtr& p)
624 {
625 return !p;
626 }
627 /** Get the raw pointer to the shadowee */
628 static T* get(const TPtr& p)
629 {
630 return p.get();
631 }
632
633 /** Convert shadowee pointer to ID for shadow cache */
635 {
636 return reinterpret_cast<TShadoweeID>(p.get());
637 }
638
639 /** Perform static cast on shadowee pointer */
640 template <typename U> static TPtr staticCast(const std::shared_ptr<U>& p)
641 {
642 return std::static_pointer_cast<T>(p);
643 }
644 /** Perform dynamic cast on shadowee pointer */
645 template <typename U> static TPtr dynamicCast(const std::shared_ptr<U>& p)
646 {
647 return std::dynamic_pointer_cast<T>(p);
648 }
649 /** Perform const cast on shadowee pointer */
650 template <typename U> static TPtr constCast(const std::shared_ptr<U>& p)
651 {
652 return std::const_pointer_cast<T>(p);
653 }
654};
655
656
657
658/** @ingroup ShadowClasses
659 * @internal
660 */
661template
662<
663 typename ShadowType,
664 typename ShadoweeType,
665 typename ParentShadowType,
666 typename PointerTraits = SharedPointerTraits<ShadoweeType>
667>
668class ShadowClass: public ParentShadowType
669{
670public:
671 typedef ShadoweeType TShadowee;
672 typedef ShadowType TShadow;
673 typedef ParentShadowType TParentShadow;
674 typedef typename PointerTraits::template Rebind<ShadoweeType>::Type TPointerTraits;
675 typedef typename PointerTraits::template Rebind<const ShadoweeType>::Type TConstPointerTraits;
676 typedef typename TPointerTraits::TPtr TShadoweePtr;
677 typedef typename TConstPointerTraits::TPtr TConstShadoweePtr;
678 typedef typename PyObjectPtr<ShadowType>::Type TShadowPtr;
679
680 static TShadowPtr make(const TShadoweePtr& shadowee)
681 {
682 return impl::makeShadow<ShadowType>(shadowee, derivedMakers_, impl::scNonConst);
683 }
684 static TShadowPtr make(const TConstShadoweePtr& shadowee)
685 {
686 return impl::makeShadow<ShadowType>(shadowee, derivedMakers_, impl::scConst);
687 }
688 static void registerWithParent()
689 {
690 ParentShadowType::registerDerivedMaker(ShadowClass::makeParent);
691 }
692
693protected:
694 typedef TShadowPtr (*TDerivedMaker)(const TConstShadoweePtr&, impl::ShadoweeConstness);
695
696 ShadowClass(const TConstShadoweePtr& shadowee, impl::ShadoweeConstness constness):
697 ParentShadowType(shadowee, constness)
698 {
699 }
700 static void registerDerivedMaker(TDerivedMaker derivedMaker)
701 {
702 impl::registerMaker(derivedMakers_, derivedMaker);
703 }
704
705private:
706 typedef std::vector<TDerivedMaker> TDerivedMakers;
707
708 typedef typename TParentShadow::TConstShadoweePtr TParentConstShadoweePtr;
709 typedef typename TParentShadow::TShadowPtr TParentShadowPtr;
710
711 static TParentShadowPtr makeParent(const TParentConstShadoweePtr& shadowee, impl::ShadoweeConstness constness)
712 {
713 const TConstShadoweePtr p = TConstPointerTraits::dynamicCast(shadowee);
714 if (TConstPointerTraits::isEmpty(p))
715 {
716 return TParentShadowPtr();
717 }
718 return impl::makeShadow<ShadowType>(p, derivedMakers_, constness);
719 }
720
721 static TDerivedMakers* derivedMakers_;
722};
723
724template <typename S, typename T, typename P, typename PT>
725typename ShadowClass<S, T, P, PT>::TDerivedMakers* ShadowClass<S, T, P, PT>::derivedMakers_ = 0;
726
727
728
729/** @ingroup ShadowClasses
730 * @internal
731 */
732template
733<
734 typename ShadowType,
735 typename ShadoweeType,
736 typename PointerTraits
737>
738class ShadowClass<ShadowType, ShadoweeType, PyObjectPlus, PointerTraits>: public impl::ShadowBaseCommon
739{
740public:
741 typedef ShadoweeType TShadowee;
742 typedef ShadowType TShadow;
743 typedef typename PointerTraits::template Rebind<ShadoweeType>::Type TPointerTraits;
744 typedef typename PointerTraits::template Rebind<const ShadoweeType>::Type TConstPointerTraits;
745 typedef typename TPointerTraits::TPtr TShadoweePtr;
746 typedef typename TConstPointerTraits::TPtr TConstShadoweePtr;
747 typedef typename PyObjectPtr<ShadowType>::Type TShadowPtr;
748
749 const TShadoweePtr cppObject() const
750 {
751 if (constness_ == impl::scConst)
752 {
753 return TShadoweePtr();
754 }
755 return TPointerTraits::constCast(shadowee_);
756 }
757 const TConstShadoweePtr& constCppObject() const
758 {
759 return shadowee_;
760 }
761 static TShadowPtr make(const TShadoweePtr& shadowee)
762 {
763 return impl::makeShadow<ShadowType>(shadowee, derivedMakers_, impl::scNonConst);
764 }
765 static TShadowPtr make(const TConstShadoweePtr& shadowee)
766 {
767 return impl::makeShadow<ShadowType>(shadowee, derivedMakers_, impl::scConst);
768 }
769 static void registerWithParent()
770 {
771 }
772protected:
773 typedef TShadowPtr (*TDerivedMaker)(const TConstShadoweePtr&, impl::ShadoweeConstness);
774 ShadowClass(const TConstShadoweePtr& shadowee, impl::ShadoweeConstness constness):
775 shadowee_(shadowee),
776 constness_(constness)
777 {
778 TConstPointerTraits::acquire(shadowee_);
779 impl::ShadowBaseCommon::registerShadowee(TConstPointerTraits::id(shadowee_), constness_);
780 }
781 ~ShadowClass()
782 {
783 impl::ShadowBaseCommon::unregisterShadowee(TConstPointerTraits::id(shadowee_), constness_);
784 TConstPointerTraits::release(shadowee_);
785 }
786 static void registerDerivedMaker(TDerivedMaker derivedMaker)
787 {
788 impl::registerMaker(derivedMakers_, derivedMaker);
789 }
790private:
791 typedef std::vector<TDerivedMaker> TDerivedMakers;
792
793 static TDerivedMakers* derivedMakers_;
794 TConstShadoweePtr shadowee_;
795 impl::ShadoweeConstness constness_;
796};
797
798template <typename S, typename T, typename PT>
799typename ShadowClass<S, T, PyObjectPlus, PT>::TDerivedMakers* ShadowClass<S, T, PyObjectPlus, PT>::derivedMakers_ = 0;
800
801
802
803/** @ingroup ShadowClasses
804 * @brief Helper to get the pointer type holding the shadowee in a shadow object
805 *
806 * @tparam ShadoweeType native C++ class, either the shadowee type being wrapped by
807 * a shadow class, or a direct exported type that derives from
808 * lass::python::PyObjectPlus.
809 *
810 * This helper gives you the correct pointer type to store a C++ class on the heap
811 * (either a shadowee type, or a direct Python class) that is compatible with the
812 * defined Python exports
813 *
814 * If @a ShadoweeType is a shadowee type, this will be the pointer type that holds
815 * the shadowee object in the shadow object, and it's defined by the pointer traits
816 * passed to PY_SHADOW_CLASS_EX() or PY_SHADOW_CLASS_PTRTRAITS()
817 * (or `SharedPointerTraits<ShadoweeType>` if you use the default PY_SHADOW_CLASS() ).
818 * Depending on the constness of the shadowee, you will either get a pointer to a
819 * non-const @a ShadoweeType or a pointer to a const @a ShadoweeType.
820 *
821 * If @a ShadoweeType is _not_ a shadowed type, but is instead a direct export and
822 * derives directly or indirectly from lass::python::PyObjectPlus, then the pointer
823 * type is simply `lass::python::PyObjectPtr<ShadoweeType>::Type`.
824 */
825template <typename ShadoweeType>
826using ShadoweePtr = std::conditional_t<std::is_const_v<ShadoweeType>,
827 typename impl::ShadowTraits<typename ShadoweeTraits<ShadoweeType>::TShadow>::TConstCppClassPtr,
828 typename impl::ShadowTraits<typename ShadoweeTraits<ShadoweeType>::TShadow>::TCppClassPtr
829>;
830
831}
832
833}
834
835/** @ingroup ShadowClasses
836 * @brief Declare Python shadow class with full control
837 *
838 * @param dllInterface desired DLL-interface of shadow class
839 * @param i_PyObjectShadowClass Unique C++ class identifier (unqualified name) of shadow class
840 * @param t_CppClass typename of the shadowee, the native C++ class being shadowed
841 * @param t_PyObjectParent the shadow's class parent (either another shadow class or lass::python::PyObjectPlus),
842 * this will be the base class of the Python type.
843 * @param t_PointerTraits complete type of pointer-traits to be used for shadowee storage.
844 */
845#define PY_SHADOW_CLASS_EX(dllInterface, i_PyObjectShadowClass, t_CppClass, t_PyObjectParent, t_PointerTraits) \
846 class dllInterface i_PyObjectShadowClass : \
847 public ::lass::python::ShadowClass< i_PyObjectShadowClass, t_CppClass, t_PyObjectParent, t_PointerTraits > \
848 { \
849 PY_HEADER(t_PyObjectParent) \
850 static void _lassPyClassRegisterHook() { registerWithParent(); } \
851 public: \
852 i_PyObjectShadowClass(const TConstShadoweePtr& shadowee, ::lass::python::impl::ShadoweeConstness constness): \
853 ::lass::python::ShadowClass< i_PyObjectShadowClass, t_CppClass, t_PyObjectParent, t_PointerTraits >(shadowee, constness) \
854 { \
855 } \
856 }; \
857 /**/
858
859/** @ingroup ShadowClasses
860 * @brief Declare Python shadow class with custom pointer traits
861 *
862 * The pointer-traits define how shadowee classes will be stored as pointer in the shadow class.
863 * Only specify the pointer-traits template name, the macro will add @a t_CppClass as template argument.
864 *
865 * @param dllInterface desired DLL-interface of shadow class
866 * @param i_PyObjectShadowClass Unique C++ class identifier (unqualified name) of shadow class
867 * @param t_CppClass typename of the shadowee, the native C++ class being shadowed
868 * @param pointerTraits shadowee pointer-traits template name
869 */
870#define PY_SHADOW_CLASS_PTRTRAITS(dllInterface, i_PyObjectShadowClass, t_CppClass, pointerTraits)\
871 PY_SHADOW_CLASS_EX(dllInterface, i_PyObjectShadowClass, t_CppClass, ::lass::python::PyObjectPlus, pointerTraits < t_CppClass > )
872
873/** @ingroup ShadowClasses
874 * @brief Declare Python shadow class with util::SharedPtr as default shadowee pointer type
875 *
876 * @param dllInterface desired DLL-interface of shadow class
877 * @param i_PyObjectShadowClass Unique C++ class identifier (unqualified name) of shadow class
878 * @param t_CppClass typename of the shadowee, the native C++ class being shadowed
879 */
880#define PY_SHADOW_CLASS(dllInterface, i_PyObjectShadowClass, t_CppClass)\
881 PY_SHADOW_CLASS_EX(dllInterface, i_PyObjectShadowClass, t_CppClass, ::lass::python::PyObjectPlus, ::lass::python::SharedPointerTraits< t_CppClass >)
882
883/** @ingroup ShadowClasses
884 * @brief Declare Python shadow child class with a parent
885 *
886 * Register a derived shadow class to reflect the same polymorphic class hierarchy in Python as in C++.
887 * If `Ham` derives from `Spam`, then a `SharedPtr<Spam>` pointer that contains a `Ham` instance will
888 * properly be returned as a `Ham` object in Python.
889 *
890 * Derived shadow classes use the same pointer traits as the parent class, so you don't need to
891 * specify it again.
892 *
893 * @param dllInterface desired DLL-interface of shadow class
894 * @param i_PyObjectShadowClass Unique C++ class identifier (unqualified name) of shadow class
895 * @param t_CppClass typename of the shadowee, the native C++ class being shadowed
896 * @param t_PyObjectShadowParent the parent's shadow class, this will be the base class of the Python type.
897 */
898#define PY_SHADOW_CLASS_DERIVED(dllInterface, i_PyObjectShadowClass, t_CppClass, t_PyObjectShadowParent)\
899 PY_SHADOW_CLASS_EX(dllInterface, i_PyObjectShadowClass, t_CppClass, t_PyObjectShadowParent, t_PyObjectShadowParent::TPointerTraits::Rebind< t_CppClass >::Type )
900
901/** @ingroup ShadowClasses
902 * @brief Deprecated alias for PY_SHADOW_CLASS_EX
903 * @deprecated Use PY_SHADOW_CLASS_EX instead, it's a direct 1:1 replacement
904 */
905#define PY_SHADOW_CLASS_NOCONSTRUCTOR_EX(dllInterface, i_PyObjectShadowClass, t_CppClass, t_PyObjectBase, t_PyObjectParent)\
906 PY_SHADOW_CLASS_EX(dllInterface, i_PyObjectShadowClass, t_CppClass, t_PyObjectBase, t_PyObjectParent)
907
908/** @ingroup ShadowClasses
909 * @brief Deprecated alias for PY_SHADOW_CLASS
910 * @deprecated Use PY_SHADOW_CLASS instead, it's a direct 1:1 replacement
911 */
912#define PY_SHADOW_CLASS_NOCONSTRUCTOR(dllInterface, i_PyObjectShadowClass, t_CppClass)\
913 PY_SHADOW_CLASS(dllInterface, i_PyObjectShadowClass, t_CppClass)
914
915/** @ingroup ShadowClasses
916 * @brief Deprecated alias for PY_SHADOW_CLASS
917 * @deprecated Use PY_SHADOW_CLASS instead, it's a direct 1:1 replacement
918 */
919#define PY_WEAK_SHADOW_CLASS(dllInterface, i_PyObjectShadowClass, t_CppClass)\
920 PY_SHADOW_CLASS(dllInterface, i_PyObjectShadowClass, t_CppClass)
921
922/** @ingroup ShadowClasses
923 * @brief Deprecated alias for PY_SHADOW_CLASS
924 * @deprecated Use PY_SHADOW_CLASS instead, it's a direct 1:1 replacement
925 */
926#define PY_WEAK_SHADOW_CLASS_NOCONSTRUCTOR(dllInterface, i_PyObjectShadowClass, t_CppClass)\
927 PY_SHADOW_CLASS(dllInterface, i_PyObjectShadowClass, t_CppClass)
928
929/** @ingroup ShadowClasses
930 * @brief Deprecated
931 * @deprecated This call has no effect and can be removed
932 */
933#define PY_SHADOW_CLASS_ENABLE_AUTOMATIC_INVALIDATION(i_PyObjectShadowClass)
934
935/** @ingroup ShadowClasses
936 * @brief Deprecated alias for PY_SHADOW_CLASS_DERIVED
937 * @deprecated Use PY_SHADOW_CLASS_DERIVED instead, it's a direct 1:1 replacement
938 */
939#define PY_SHADOW_CLASS_DERIVED_NOCONSTRUCTOR(dllInterface, i_PyObjectShadowClass, t_CppClass, t_PyObjectShadowParent)\
940 PY_SHADOW_CLASS_DERIVED(dllInterface, i_PyObjectShadowClass, t_CppClass, t_PyObjectShadowParent)
941
942
943
944
945/** @ingroup ShadowClasses
946 * @brief Define lass::python::ShadoweeTraits for shadow class
947 *
948 * This ensures that shadowee class becomes usable as parameter or return type.
949 * If `PySpam` is @a t_ShadowObject, the shadow class of `Spam`, then `Spam`,
950 * `const Spam&`, `Spam*`, `lass::util::SharedPtr<Spam>`, etc. all become
951 * usable as parameter or return types.
952 *
953 * @param t_ShadowObject fully qualified typename of Python Shadow class
954 *
955 * @note This macro **MUST** be invoked in the global namespace
956 *
957 * @note This macro **MUST** be invoked in the same header that declares the
958 * shadow class with PY_SHADOW_CLASS or similar. Every translation unit
959 * that uses the shadow class must include it.
960 */
961#define PY_SHADOW_CASTERS(t_ShadowObject)\
962namespace lass \
963{ \
964namespace python \
965{ \
966 template <> struct ShadoweeTraits< t_ShadowObject::TShadowee >: ::lass::meta::True \
967 { \
968 typedef t_ShadowObject TShadow; \
969 /*typedef impl::ShadowTraits< t_ShadowObject > TShadowTraits;*/ \
970 typedef t_ShadowObject::TPointerTraits TPointerTraits; \
971 }; \
972} \
973} \
974/**/
975
976
977
978/** @ingroup ShadowClasses
979 * @brief Deprecated alias for PY_SHADOW_CASTERS
980 * @deprecated Use PY_SHADOW_CASTERS instead, it's a direct 1:1 replacement
981 */
982#define PY_SHADOW_DOWN_CASTERS(t_ShadowObject)\
983 PY_SHADOW_CASTERS(t_ShadowObject)
984
985/** @ingroup ShadowClasses
986 * @brief Deprecated alias for PY_SHADOW_CASTERS
987 * @deprecated Use PY_SHADOW_CASTERS instead, it's a direct 1:1 replacement
988 */
989#define PY_SHADOW_DOWN_CASTERS_NOCONSTRUCTOR(t_ShadowObject)\
990 PY_SHADOW_CASTERS(t_ShadowObject)
991
992
993
994/** @ingroup ShadowClasses
995 * @brief Add implicit conversion function to Python class
996 *
997 * Add a conversion function to a Python class to implicitly convert a Python object to the C++ class when it is being
998 * passed as a parameter to a C++ function
999 *
1000 * By default, you need to convert types by explicitly constructing an object in Python, or you must overload the
1001 * function on different types. By adding implicit convertors, they will automatically be attempted when
1002 * calling functions taking the native C++ class. If conversion is successful, the function is called.
1003 *
1004 * The conversion function must be of the following signature: `int f(PyObject* obj, ShadoweePtr<T>& val)`.
1005 * It receives a pointer to the Python object for which the conversion must be attempted.
1006 * If successful, you should construct a new instance on the heap of the native C++ class, assign it to the shadowee
1007 * pointer, and return 0. If failed, you should return 1, and a generic `TypeError` will be set.
1008 *
1009 * Conversions are attempted in the order of registration.
1010 *
1011 * This macro must be invoked in the same translation unit (*.cpp file) as `PY_DECLARE_CLASS_*`.
1012 *
1013 * @param t_ShadowObject Python class (fully qualified) on which to add the convertor
1014 * @param f_conversionFunction conversion function
1015 * @param i_uniqueName identifier used to name the generated registration hook
1016 *
1017 * @par Example:
1018 *
1019 * ```cpp
1020 * class Spam
1021 * {
1022 * public:
1023 * Spam(const std::string& s);
1024 * };
1025 *
1026 * int spamConversion(PyObject* obj, lass::python::ShadoweePtr<Spam>& spam)
1027 * {
1028 * std::string s;
1029 * if (::lass::python::pyGetSimpleObject(obj, s) == 0)
1030 * {
1031 * spam.reset(new Spam(s));
1032 * return 0; // conversion succeeded
1033 * }
1034 * return 1; // conversion failed
1035 * }
1036 *
1037 * PY_SHADOW_CLASS(LASS_DLL_EXPORT, PySpam, Spam)
1038 * PY_SHADOW_CASTERS(PySpam)
1039 * PY_DECLARE_CLASS_NAME(PySpam, "Spam")
1040 * PY_CLASS_CONSTRUCTOR_1(PySpam, const std::string&)
1041 * PY_CLASS_CONVERTOR_EX(PySpam, spamConversion, spam)
1042 *
1043 * void func(const Spam& spam);
1044 * PY_MODULE_CLASS(mod, PySpam)
1045 * PY_MODULE_FUNCTION(mod, func)
1046 * ```
1047 *
1048 * ```py
1049 * mod.func(mod.Spam("abc")) # explicit conversion using constructor
1050 * mod.func("abc") # implicit conversion using convertor
1051 * ```
1052 *
1053 * @sa PY_CLASS_CONVERTOR for adding auto-convertors using the constructor
1054 */
1055#define PY_CLASS_CONVERTOR_EX( t_ShadowObject, f_conversionFunction, i_uniqueName )\
1056 LASS_EXECUTE_BEFORE_MAIN_EX( LASS_CONCATENATE( lassPyClassConverter_, i_uniqueName ),\
1057 lass::python::impl::ShadowTraits< t_ShadowObject >::addConverter( f_conversionFunction );\
1058 )
1059
1060/** @ingroup ShadowClasses
1061 * @brief Add implicit auto-conversion to Python class
1062 *
1063 * Add an implicit convertor from @a t_sourceType to the Python class @a i_ShadowObject.
1064 * It is attempted when a Python object that is not already an instance of that class is passed
1065 * to a C++ function taking the C++ class as a parameter: the object is first converted to an
1066 * instance of @a t_sourceType, from which a new instance of the C++ class is constructed.
1067 * This assumes the C++ class has a matching constructor.
1068 *
1069 * Without it, you must convert the value explicitly in Python by constructing an instance of the
1070 * Python class, or the C++ function must be overloaded on @a t_sourceType.
1071 *
1072 * Conversions are attempted in the order of registration.
1073 *
1074 * This macro must be invoked in the same translation unit (*.cpp file) as `PY_DECLARE_CLASS_*`.
1075 *
1076 * @param i_ShadowObject Python class identifier (unqualified) on which to add the auto-convertor
1077 * @param t_sourceType type from which to convert. It must be default-constructible and be
1078 * exported to Python as a class or with `PyExportTraits`.
1079 *
1080 * @par Example:
1081 *
1082 * ```cpp
1083 * class Spam
1084 * {
1085 * public:
1086 * Spam(const std::string& s);
1087 * };
1088 *
1089 * PY_SHADOW_CLASS(LASS_DLL_EXPORT, PySpam, Spam)
1090 * PY_SHADOW_CASTERS(PySpam)
1091 * PY_DECLARE_CLASS_NAME(PySpam, "Spam")
1092 * PY_CLASS_CONSTRUCTOR_1(PySpam, const std::string&)
1093 * PY_CLASS_CONVERTOR(PySpam, std::string)
1094 *
1095 * void func(const Spam& spam);
1096 * PY_MODULE_CLASS(mod, PySpam)
1097 * PY_MODULE_FUNCTION(mod, func)
1098 * ```
1099 *
1100 * ```py
1101 * mod.func(mod.Spam("abc")) # explicit conversion using constructor
1102 * mod.func("abc") # implicit conversion using convertor
1103 * ```
1104 *
1105 * @sa PY_CLASS_CONVERTOR_EX for adding convertors with custom conversion functions
1106 */
1107#define PY_CLASS_CONVERTOR( i_ShadowObject, t_sourceType )\
1108 PY_CLASS_CONVERTOR_EX( i_ShadowObject, (::lass::python::impl::defaultConvertor< i_ShadowObject, t_sourceType >) , i_ShadowObject );
1109
1110
1111
1112#endif
1113
1114// EOF
The default counter for the shared pointers, implementation of CounterPolicy concept.
Default storage policy for single objects, implementation of StoragePolicy concept.
PyObjectPtr< PyObject >::Type TPyObjPtr
PyObjectPtr to a PyObject.
lass::util::SharedPtr< T, PyObjectStorage, PyObjectCounter > fromNakedToSharedPtrCast(PyObject *object)
fromNakedToSharedPtrCast.
num::TuintPtr TShadoweeID
ID-type to uniquely identify shadowee instances in the shadow cache.
std::conditional_t< std::is_const_v< ShadoweeType >, typename impl::ShadowTraits< typename ShadoweeTraits< ShadoweeType >::TShadow >::TConstCppClassPtr, typename impl::ShadowTraits< typename ShadoweeTraits< ShadoweeType >::TShadow >::TCppClassPtr > ShadoweePtr
Helper to get the pointer type holding the shadowee in a shadow object.
Comprehensive C++ to Python binding library.
Library for Assembled Shared Sources.
Definition config.h:53
Rebind pointer traits to other shadowee type.
Pointer-traits for Python Shadow classes that use raw * pointers for storage.
static void release(TPtr)
Raw pointers have no ownership rules.
static TPtr staticCast(U *p)
Perform static cast on shadowee pointer.
static void acquire(TPtr)
Raw pointers have no ownership rules.
static TPtr dynamicCast(U *p)
Perform dynamic cast on shadowee pointer.
static TPtr constCast(U *p)
Perform const cast on shadowee pointer.
static T * get(TPtr p)
Get the raw pointer to the shadowee.
T * TPtr
Pointer to shadowee type.
static TShadoweeID id(TPtr p)
Convert shadowee pointer to ID for shadow cache.
static bool isEmpty(TPtr p)
Return true when storing a nullptr.
Rebind pointer traits to other shadowee type.
Pointer-traits for Python Shadow classes that use lass::util::SharedPtr for storage.
static bool isEmpty(const TPtr &p)
Return true when storing a nullptr.
static void acquire(const TPtr &)
util::SharedPtr already handles reference counts
static TPtr dynamicCast(const util::SharedPtr< U, S, C > &p)
Perform dynamic cast on shadowee pointer.
util::SharedPtr< ContainerType, util::ObjectStorage, util::DefaultCounter > TPtr
static void release(const TPtr &)
util::SharedPtr already handles reference counts
static T * get(const TPtr &p)
Get the raw pointer to the shadowee.
static TPtr staticCast(const util::SharedPtr< U, S, C > &p)
Perform static cast on shadowee pointer.
static TShadoweeID id(const TPtr &p)
Convert shadowee pointer to ID for shadow cache.
static TPtr constCast(const util::SharedPtr< U, S, C > &p)
Perform const cast on shadowee pointer.
Rebind pointer traits to other shadowee type.
Pointer-traits for Python Shadow classes that use std::shared_ptr for storage.
static void acquire(const TPtr &)
std::shared_ptr already handles reference counts
static void release(const TPtr &)
std::shared_ptr already handles reference counts
static TPtr constCast(const std::shared_ptr< U > &p)
Perform const cast on shadowee pointer.
static TShadoweeID id(TPtr p)
Convert shadowee pointer to ID for shadow cache.
static bool isEmpty(const TPtr &p)
Return true when storing a nullptr.
static TPtr staticCast(const std::shared_ptr< U > &p)
Perform static cast on shadowee pointer.
static TPtr dynamicCast(const std::shared_ptr< U > &p)
Perform dynamic cast on shadowee pointer.
static T * get(const TPtr &p)
Get the raw pointer to the shadowee.
std::shared_ptr< T > TPtr
Pointer to shadowee type.