Library of Assembled Shared Sources
 
Loading...
Searching...
No Matches
export_traits.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-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#ifndef LASS_GUARDIAN_OF_INCLUSION_PYTHON_EXPORT_TRAITS_H
44#define LASS_GUARDIAN_OF_INCLUSION_PYTHON_EXPORT_TRAITS_H
45
46#include "python_common.h"
47#include "shadowee_traits.h"
48#include "exception.h"
49#include "pyobject_ptr.h"
50#include "no_none.h"
51#include "maybe_none.h"
52#include "self.h"
53#include "subscript.h"
54#include "../num/num_cast.h"
55
56namespace lass
57{
58namespace python
59{
60
61/** @defgroup PyExportTraits
62 * @ingroup Python
63 * @brief Traits to convert between C++ and Python types
64 *
65 * `PyExportTraits<T>` is a central part of the Lass Python binding system.
66 * It defines how to convert between a C++ type `T` and a corresponding Python object.
67 * It also provides type hinting information for Python type annotations.
68 *
69 * Client code will not usually use these traits directly, but rather use the functions
70 * `pyBuildSimpleObject()` and `pyGetSimpleObject()`:
71 *
72 * ```
73 * TPyObjPtr pyObj( pyBuildSimpleObject(cppValue) );
74 * if (!pyObj)
75 * // error occurred, Python exception set
76 *
77 * T cppValue2;
78 * if (pyGetSimpleObject(pyObj.get(), cppValue2) != 0)
79 * // error occurred, Python exception set
80 * ```
81 *
82 * A well-defined `PyExportTraits<T>` specialization provides the following parts,
83 * all of which are optional:
84 *
85 * - Python type hinting information: `py_typing`, `py_typing_param`, `py_typing_preamble`
86 * - A static method `build(const T& value)` that converts a C++ value to a new Python object.
87 * - A static method `get(PyObject* obj, T& value)` that converts a Python object to a C++ value.
88 *
89 * ```
90 * template <typename T>
91 * struct PyExportTraits<Spam<T>>
92 * {
93 * constexpr static const char* py_typing = "Spam[T]";
94 * constexpr static const char* py_typing_param = "_Spam[T]";
95 * constexpr static const char* py_typing_preamble = "type _Spam[T] = Spam[T] | T";
96 *
97 * static PyObject* build(const Spam<T> &value)
98 * {
99 * // Construct PyObject from value and return new reference.
100 * // Or set Python error and return nullptr on failure.
101 * }
102 * static int get(PyObject* obj, Spam<T>& value)
103 * {
104 * // Extract and set value from obj, and return 0 on success.
105 * // Or set Python error and return -1 on failure.
106 * }
107 * }
108 * ```
109 *
110 *
111 *
112 * ### Building Python objects from C++ values
113 *
114 * The `build()` method should return a new reference to a Python object that represents the C++
115 * value passed as argument. If the conversion fails, it should set a Python exception and
116 * return nullptr.
117 *
118 * Its return type must be `PyObject*`, and it must accept a single argument of type `const T&`.
119 *
120 * If a `PyExportTraits<T>` specialization does not provide a `build()` method, then the C++
121 * type cannot be converted to Python. This is only useful for types that can only be used as
122 * function parameters, but not as return values or attributes.
123 *
124 * Client code will usually not call the `build()` method directly, but rather use the
125 * `pyBuildSimpleObject()` function, which will call the `build()` method internally.
126 *
127 *
128 *
129 * ### Getting C++ values from Python objects
130 *
131 * The `get()` method should extract the C++ value from the Python object passed as argument,
132 * and store it in the second argument passed by reference. If the conversion is successful,
133 * it should return 0. If the conversion fails, it should set a Python exception and return 1.
134 *
135 * The first argument must be of type `PyObject*`, and the `get()` shall _not_ eat a reference.
136 * In other words, the `get()` method gets a borrowed reference to the Python object.
137 *
138 * The second argument must be of type `T&`, and the `get()` method should set this value if
139 * conversion is successful.
140 *
141 * @note Some types cannot define a meaningful `get()` method, such as types that cannot be
142 * default-constructed, or types that don't have a well-defined lifetime, such as `char*` strings.
143 * In such cases, the `PyExportTraits<T>` specialization should not provide a `get()` method.
144 * But that does not mean that these types cannot be used as function parameters. By specializing
145 * `ArgumentTraits` for these types, you can still support them as function parameters, and it may
146 * still make sense to define `py_typing_param` for type hinting. See `ArgumentTraits` for more
147 * details on techniques to support such types as function parameters.
148 *
149 *
150 *
151 * ### Type Hinting
152 *
153 * The three type hinting members are used in combination with lass_stubgen to automatically
154 * generate Python stub files (.pyi) that provide type hinting information for Python extension
155 * modules that use the Lass binding system.
156 *
157 * All three members are of type `constexpr static const char*`, and are optional:
158 *
159 * - `py_typing`: the type in Python typing syntax.
160 * - `py_typing_param`: the Python type when used as a function parameter.
161 * - `py_typing_preamble`: additional Python code that needs to be included to support the type
162 * hinting, such as type aliases or imports.
163 *
164 *
165 * #### `py_typing`
166 *
167 * To generate useful type hints, you need to provide at least the `py_typing` member. This should
168 * be a string that describes the type in Python typing syntax. For example, for the C++ type
169 * `prim::ColorRGBA` will always convert to a tuple of 4 floats `(r, g, b, a)`, and can be
170 * described as:
171 * ```
172 * template <>
173 * struct PyExportTraits<prim::ColorRGBA>
174 * {
175 * constexpr static const char* py_typing = "tuple[float, float, float, float]";
176 * };
177 * ```
178 *
179 *
180 * #### `py_typing_param`
181 *
182 * When `py_typing_param` is provided, it will be used instead of `py_typing` when the type is used
183 * as a function parameter. This is useful for types that can be converted from additional types
184 * when used as a parameter. For example, `prim::ColorRGBA` will always convert to a tuple of 4
185 * floats `(r, g, b, a)` when returned from a function, but it will also accept `(r, g, b), a tuple
186 * of 3 floats without the alpha channel `(r, g, b)` as argument:
187 * ```
188 * template <>
189 * struct PyExportTraits<prim::ColorRGBA>
190 * {
191 * constexpr static const char* py_typing = "tuple[float, float, float, float]";
192 * constexpr static const char* py_typing_param = "tuple[float, float, float, float] | tuple[float, float, float]";
193 * };
194 * ```
195 *
196 *
197 * #### `py_typing_preamble`
198 *
199 * `py_typing_preamble` can be used to provide additional Python code that needs to be added to the
200 * generated stub files to support the type hinting. This can be used to define type aliases to
201 * make the type hints more readable. For example, for `prim::ColorRGBA`, we define two aliases
202 * `_RGBA` and `_RGB` to make the type hints more readable. The preamble can consist of multiple
203 * lines, separated by newline characters. Each type alias will be of the form `type _Name = ...`:
204 * ```
205 * template <>
206 * struct PyExportTraits<prim::ColorRGBA>
207 * {
208 * constexpr static const char* py_typing = "_RGBA";
209 * constexpr static const char* py_typing_param = "_RGBA | _RGB";
210 * constexpr static const char* py_typing_preamble =
211 * "type _RGBA = tuple[float, float, float, float]\n"
212 * "type _RGB = tuple[float, float, float]";
213 * };
214 * ```
215 *
216 * The preamble can also be used to include necessary imports. For example, `std::filesystem::path`
217 * is mapped to `pathlib.Path` in Python, so the `pathlib` module needs to be imported. And as
218 * parameter, it also accepts `str` and `bytes`, or anything that implements the `os.PathLike`
219 * for which the `StrOrBytesPath` type alias from the `_typeshed` module is used:
220 *
221 * ```
222 * template <>
223 * struct PyExportTraits<std::filesystem::path>
224 * {
225 * constexpr static const char* py_typing = "pathlib.Path";
226 * constexpr static const char* py_typing_param = "StrOrBytesPath";
227 * constexpr static const char* py_typing_preamble = "import pathlib\nfrom _typeshed import StrOrBytesPath";
228 * };
229 * ```
230 *
231 * @note Type-aliases should start with an underscore to indicate they are private and not really
232 * part of the module API. They only exist for the purpose of type hinting and checking.
233 * Attempting to import them at runtime will result in a failure, unless you hide the code
234 * in a `if TYPE_CHECKING:` block.
235 * See https://docs.python.org/3/library/typing.html#typing.TYPE_CHECKING
236 *
237 * @note It is also *your* responsibility to ensure that the type aliases do not conflict with any
238 * other names in the module or in the Python standard library.
239 *
240 * @note The preamble can only contain type aliases and imports.
241 *
242 *
243 * #### Partial Specializations
244 *
245 * If a `PyExportTraits` is partially specialized, such as for template classes, then the
246 * template parameters can be used in the `py_typing` and `py_typing_param` members. They will be
247 * substituted with the corresponding Python type hints of the template parameters. For example:
248 * for the C++ type `std::pair<T, U>` translates to a Python tuple of two elements, and is
249 * described as:
250 * ```
251 * template <typename T, typename U>
252 * struct PyExportTraits<std::pair<T, U>>
253 * {
254 * constexpr static const char* py_typing = "tuple[T, U]";
255 * };
256 * ```
257 *
258 * When substituing template parameters in the context of function parameters, then
259 * `py_typing_param` is used if the template argument defines it, even if the top-level
260 * `PyExportTraits` specialization only defines `py_typing`. For example, `std::pair<T, U>` only
261 * defines `py_typing`, but `float` defines the alias `_Float` as `py_typing_param`. So a
262 * function taking `std::pair<float, float>` as a parameter would have the following type hint:
263 * ```
264 * def func(param: tuple[_Float, _Float]) -> None:
265 * ...
266 * ```
267 *
268 * But sometimes, you really want to substitute the template parameters by their `py_typing` type,
269 * even in the context of function parameters. In that case, you can add an exlamation mark `!`
270 * after the template parameter name as special syntax to indicate that you always want to replace
271 * it by its `py_typing` type.
272 *
273 * One example where this is useful is callable types, such as `std::function<R(T, U)>`. What
274 * you're really saying when a function takes a `std::function<R(T, U)>` as parameter, is that
275 * it takes a callable that accepts two parameters of type `T` and `U`, and that you will call
276 * that callable from C++, you are providing the arguments of type `T` and `U` from C++ to Python,
277 * similar like returning values from a normal function. So in this case, you want the types of
278 * the T and U arguments to be substituted by their `py_typing` type, not their `py_typing_param`
279 * type. So you would define the `PyExportTraits` specialization as:
280 * ```
281 * template <typename R, typename T, typename U>
282 * struct PyExportTraits<std::function<R(T, U)>>
283 * {
284 * constexpr static const char* py_typing = "Callable[[T!, U!], R!]";
285 * };
286 * ```
287 */
288
289
290namespace impl
291{
292 template <typename T> struct ShadowTraits;
293
294 template <typename In, typename Out>
295 [[deprecated]] int pyNumCast(In in, Out& out)
296 {
297 try
298 {
299 out = num::numCast<Out>(in);
300 }
301 catch (const std::exception& error)
302 {
303 PyErr_SetString(PyExc_TypeError, error.what());
304 return 1;
305 }
306 return 0;
307 }
308
309 template <typename T>
310 using NoNone [[deprecated("lass::python::NoNone<T> instead.")]] = lass::python::NoNone<T>;
311
312 LASS_PYTHON_DLL PyObject* buildStringImpl(const char* s, size_t n);
313 LASS_PYTHON_DLL PyObject* buildStringImpl(const wchar_t* s, size_t n);
314#if LASS_HAVE_STD_U8STRING
315#if __cpp_lib_char8_t
316 LASS_PYTHON_DLL PyObject* buildStringImpl(const char8_t* s, size_t n);
317#endif
318#endif
319 LASS_PYTHON_DLL PyObject* buildStringImpl(const char16_t* s, size_t n);
320 LASS_PYTHON_DLL PyObject* buildStringImpl(const char32_t* s, size_t n);
321}
322
323/** by copy, general case assumes shadow type or PyObjectPlus based type.
324 * @ingroup PyExportTraits
325 */
326template <typename T>
328{
329 typedef impl::ShadowTraits<typename ShadoweeTraits<T>::TShadow> TShadowTraits;
330 static PyObject* build(const T& v)
331 {
332 return fromSharedPtrToNakedCast(TShadowTraits::buildObject(v));
333 }
334 static int get(PyObject* obj, T& v)
335 {
336 return TShadowTraits::getObject(obj, v);
337 }
338};
339
340/** constant objects can only be build.
341 * @ingroup PyExportTraits
342 */
343template <typename T>
344struct PyExportTraits<const T>
345{
346 static PyObject* build(const T& v)
347 {
348 return PyExportTraits<T>::build(v);
349 }
350};
351
352
353// --- pointers ------------------------------------------------------------------------------------
354
355/** @ingroup PyExportTraits
356 */
357template <typename T>
358struct PyExportTraits< T* >
359{
360 typedef impl::ShadowTraits<typename ShadoweeTraits<T>::TShadow> TShadowTraits;
361 static PyObject* build(T* value)
362 {
363 if (!value)
364 {
365 Py_RETURN_NONE;
366 }
367 return fromSharedPtrToNakedCast(TShadowTraits::buildObject(value));
368 }
369 static int get(PyObject* obj, T*& value)
370 {
371 if (obj == Py_None)
372 {
373 value = 0;
374 return 0;
375 }
376 return TShadowTraits::getObject(obj, value);
377 }
378};
379
380/** SharedPtr assumes shadow types or PyObjectPlus types.
381 *
382 * If it's a nullptr, it is mapped to `None`.
383 * Otherwise, it is mapped to the actual Python object, increasing the reference count.
384 *
385 * When getting a SharedPtr from Python, `None` is mapped to a nullptr.
386 * Otherwise it has to be of the correct type, and the SharedPtr will share ownership of the object.
387 *
388 * @ingroup PyExportTraits
389 */
390template <typename T, template <typename, typename> class S, typename C>
391struct PyExportTraits< util::SharedPtr<T, S, C> >
392{
393 constexpr static const char* py_typing = "T | None";
394
395 typedef impl::ShadowTraits<typename ShadoweeTraits<T>::TShadow> TShadowTraits;
396 typedef util::SharedPtr<T, S, C> TPtr;
397 static PyObject* build(const TPtr& value)
398 {
399 if (!value)
400 {
401 Py_RETURN_NONE;
402 }
403 return fromSharedPtrToNakedCast(TShadowTraits::buildObject(value));
404 }
405 static int get(PyObject* obj, TPtr& value)
406 {
407 if (obj == Py_None)
408 {
409 value = TPtr();
410 return 0;
411 }
412 return TShadowTraits::getObject(obj, value);
413 }
414};
415
416
417/** A shared `PyObject` pointer is mapped to `Any` in Python.
418 *
419 * It's mapped to the actual Python object, increasing the reference count,
420 * and a nullptr is mapped to `None`.
421 *
422 * @note When **getting** a `None` object from Python, you will get a TPyObjPtr holding
423 * `Py_None`. So `v` will **never** be null!
424 *
425 * @ingroup PyExportTraits
426 */
427template <>
429{
430 constexpr static const char* py_typing = "Any";
431
432 static PyObject* build(const TPyObjPtr& v)
433 {
434 if (!v)
435 {
436 Py_RETURN_NONE;
437 }
438 return fromSharedPtrToNakedCast(v);
439 }
440 static int get(PyObject* obj, TPyObjPtr& v)
441 {
443 return 0;
444 }
445};
446
447
448/** @ingroup PyExportTraits
449 */
450/*
451template <typename T>
452struct PyExportTraits< util::SharedPtr<T, PyObjectStorage, PyObjectCounter> >
453{
454 static PyObject* build( const util::SharedPtr<T, PyObjectStorage, PyObjectCounter>& iV )
455 {
456 if (!iV)
457 {
458 Py_RETURN_NONE;
459 }
460 return fromSharedPtrToNakedCast(iV);
461 }
462 static int get(PyObject* iValue, util::SharedPtr<T, PyObjectStorage, PyObjectCounter>& oV)
463 {
464 const bool isNone = (iValue == Py_None );
465 if (isNone)
466 {
467 oV = util::SharedPtr<T, PyObjectStorage, PyObjectCounter>();
468 }
469 else
470 {
471 if (!PyType_IsSubtype(iValue->ob_type , T::_lassPyClassDef.type() ))
472 {
473 PyErr_Format(PyExc_TypeError,"not castable to %s",T::_lassPyClassDef.name() );
474 return 1;
475 }
476 oV = fromNakedToSharedPtrCast<T>(iValue);
477 }
478 return 0;
479 }
480};
481*/
482
483
484/** std::unique_ptr assumes shadow types
485* @ingroup PyExportTraits
486*/
487template <typename T, typename Deleter>
488struct PyExportTraits< std::unique_ptr<T, Deleter> >
489{
490 constexpr static const char* py_typing = "T | None";
491
492 typedef impl::ShadowTraits<typename ShadoweeTraits<T>::TShadow> TShadowTraits;
493 static PyObject* build(std::unique_ptr<T, Deleter>&& value)
494 {
495 if (!value)
496 {
497 Py_RETURN_NONE;
498 }
499 return fromSharedPtrToNakedCast(TShadowTraits::buildObject(std::move(value)));
500 }
501};
502
503
504/** std::shared_ptr assumes shadow types.
505 * @ingroup PyExportTraits
506 */
507template <typename T>
508struct PyExportTraits< std::shared_ptr<T> >
509{
510 constexpr static const char* py_typing = "T | None";
511
512 typedef impl::ShadowTraits<typename ShadoweeTraits<T>::TShadow> TShadowTraits;
513 typedef std::shared_ptr<T> TPtr;
514 static PyObject* build(const TPtr& value)
515 {
516 if (!value)
517 {
518 Py_RETURN_NONE;
519 }
520 return fromSharedPtrToNakedCast(TShadowTraits::buildObject(value));
521 }
522 static int get(PyObject* obj, TPtr& value)
523 {
524 if (obj == Py_None)
525 {
526 value.reset();
527 return 0;
528 }
529 return TShadowTraits::getObject(obj, value);
530 }
531};
532
533// --- void ptrs ------------------------------------------------------------------------------------
534
535/** @ingroup PyExportTraits
536 */
537template <>
538struct PyExportTraits<void*>
539{
540 constexpr static const char* py_typing = "Any"; // should be CapsuleType | None instead?
541
542 LASS_PYTHON_DLL static PyObject* build(void* value);
543 LASS_PYTHON_DLL static int get(PyObject* obj, void*& value);
544};
545
546
547
548/** @ingroup PyExportTraits
549 */
550template <>
551struct PyExportTraits<std::nullptr_t>
552{
553 constexpr static const char* py_typing = "None";
554
555 LASS_PYTHON_DLL static PyObject* build(std::nullptr_t value);
556 LASS_PYTHON_DLL static int get(PyObject* obj, std::nullptr_t& value);
557};
558
559
560
561// --- NoNone --------------------------------------------------------------------------------------
562
563/** Helper class to create PyExportTraits for NoNone wrapped types.
564 *
565 * NoNone wrapped types are used to ensure that:
566 * - No `None` values can be passed from Python to C++ (which would be translated to a `nullptr`),
567 * - No `nullptr` values can be returned to Python (which would be translated to a `None`).
568 *
569 * Use this helper to create PyExportTraits for your own NoNone wrapped types by inheriting from it.
570 *
571 * ```
572 * template <typename T, template <typename, typename> class S, typename C>
573 * struct PyExportTraits< NoNone< util::SharedPtr<T, S, C> > >: public PyExportTraitsNoNone< util::SharedPtr<T, S, C> >
574 * {
575 * constexpr static const char* py_typing = "T"; // optional
576 * };
577 * ```
578 *
579 * Built-in specializations are provided for raw pointers `T*`, util::SharedPtr<T>, and `std::shared_ptr<T>`.
580 *
581 * @note If you use `typename T` as the main template parameter for your actual type, then the
582 * Python type will automatically be deduced. If not, or if you still see a `T | None` type-hint,
583 * you can can override the `py_typing` member to specify the type-hint you want.
584 *
585 * @ingroup PyExportTraits NoNone
586 * @sa NoNone
587 * @sa PyExportTraits<NoNone<T*>>
588 * @sa PyExportTraits<NoNone<util::SharedPtr<T,S,C>>>
589 * @sa PyExportTraits<NoNone<std::shared_ptr<T>>>
590 */
591template <typename T>
593{
594 constexpr static const char* py_typing = "T";
595
596 /** Raise a Python `TypeError` if @a value is equal to `nullptr` */
597 static PyObject* build(const NoNone<T>& value)
598 {
599 const T& v = static_cast<const T&>(value);
600 if (v == nullptr)
601 {
602 PyErr_SetString(PyExc_TypeError, "value must be not be None");
603 return nullptr;
604 }
605 return PyExportTraits<T>::build(value);
606 }
607 /** Raise a Python `TypeError` if @a obj is equal to `None` */
608 static int get(PyObject* obj, NoNone<T>& value)
609 {
610 if (obj == Py_None)
611 {
612 PyErr_SetString(PyExc_TypeError, "argument must be not be None");
613 return 1;
614 }
615 return PyExportTraits<T>::get(obj, value);
616 }
617};
618
619/** NoNone<T*> type-hints as `T` and refuses `None` as value.
620 *
621 * @ingroup PyExportTraits NoNone
622 * @sa NoNone
623 * @sa PyExportTraitsNoNone
624 */
625template <typename T>
627{
628};
629
630/** Type-hints NoNone<util::SharedPtr<T>> as `T` and refuses `None` as value.
631 *
632 * @ingroup PyExportTraits NoNone
633 * @sa NoNone
634 * @sa util::SharedPtr
635 * @sa PyExportTraitsNoNone
636 */
637template <typename T, template <typename, typename> class S, typename C>
638struct PyExportTraits< NoNone< util::SharedPtr<T, S, C> > > : public PyExportTraitsNoNone< util::SharedPtr<T, S, C> >
639{
640};
641
642/** NoNone<std::shared_ptr<T>> type-hints as `T` and refuses `None` as value.
643 *
644 * @ingroup PyExportTraits NoNone
645 * @sa NoNone
646 * @sa PyExportTraitsNoNone
647 */
648template <typename T>
649struct PyExportTraits< NoNone< std::shared_ptr<T> > > : public PyExportTraitsNoNone< std::shared_ptr<T> >
650{
651};
652
653
654
655// --- MaybeNone -----------------------------------------------------------------------------------
656
657/** Helper class to create PyExportTraits for MaybeNone wrapped types.
658 *
659 * MaybeNone does not provide any runtime or compile-time checks. It only serves to alter the
660 * type-hint to `T | MaybeNone` in Python, which is useful for type-checking, see [The Any Trick].
661 *
662 * MaybeNone<T> types can only be returned from C++ to Python, it doesn't make sense to pass them
663 * as function parameters to C++.
664 *
665 * Use this helper to create PyExportTraits for your own MaybeNone wrapped types by inheriting from it.
666 *
667 * ```
668 * template <typename T, template <typename, typename> class S, typename C>
669 * struct PyExportTraits< MaybeNone< util::SharedPtr<T, S, C> > >: public PyExportTraitsMaybeNone< util::SharedPtr<T, S, C> >
670 * {
671 * constexpr static const char* py_typing = "T | MaybeNone"; // optional
672 * };
673 * ```
674 *
675 * Built-in specializations are provided for raw pointers `T*`, util::SharedPtr<T>, `std::shared_ptr<T>`,
676 * and `std::optional<T>`.
677 *
678 * @note If you use `typename T` as the main template parameter for your actual type, then the
679 * Python type will automatically be deduced. If not, or if you still see a `T | None` type-hint,
680 * you can can override the `py_typing` member to specify the type-hint you want.
681 *
682 * [The Any Trick]: https://typing.python.org/en/latest/guides/writing_stubs.html#the-any-trick
683 *
684 * @ingroup PyExportTraits
685 * @sa MaybeNone
686 * @sa PyExportTraits<MaybeNone<T*>>
687 * @sa PyExportTraits<MaybeNone<util::SharedPtr<T,S,C>>>
688 * @sa PyExportTraits<MaybeNone<std::shared_ptr<T>>>
689 * @sa PyExportTraits<MaybeNone<std::optional<T>>>
690 */
691template <typename T>
693{
694 constexpr static const char* py_typing = "T | MaybeNone";
695 constexpr static const char* py_typing_preamble = "from _typeshed import MaybeNone";
696
697 static PyObject* build(const MaybeNone<T>& value)
698 {
699 return PyExportTraits<T>::build(value);
700 }
701};
702
703/** MaybeNone<T*> type-hints a type as `T | MaybeNone`
704 *
705 * @ingroup PyExportTraits
706 * @sa MaybeNone
707 * @sa PyExportTraitsMaybeNone
708 */
709template <typename T>
711{
712};
713
714/** MaybeNone<util::SharedPtr<T>> type-hints a type as `T | MaybeNone`
715 *
716 * @ingroup PyExportTraits
717 * @sa MaybeNone
718 * @sa util::SharedPtr
719 * @sa PyExportTraitsMaybeNone
720 */
721template <typename T, template <typename, typename> class S, typename C>
722struct PyExportTraits< MaybeNone< util::SharedPtr<T, S, C> > > : public PyExportTraitsMaybeNone< util::SharedPtr<T, S, C> >
723{
724};
725
726/** MaybeNone<std::shared_ptr<T>> type-hints a type as `T | MaybeNone`
727 *
728 * @ingroup PyExportTraits
729 * @sa MaybeNone
730 * @sa PyExportTraitsMaybeNone
731 */
732template <typename T>
733struct PyExportTraits< MaybeNone< std::shared_ptr<T> > > : public PyExportTraitsMaybeNone< std::shared_ptr<T> >
734{
735};
736
737
738
739// --- Self --------------------------------------------------------------------------------------
740
741/** Self<T> type-hints as `Self`.
742 *
743 * @ingroup PyExportTraits
744 * @sa Self
745 */
746template <typename T>
747struct PyExportTraits< Self<T> > : public PyExportTraits<T>
748{
749 constexpr static const char* py_typing = "Self";
750#if PY_VERSION_HEX < 0x030b0000 // < 3.11
751 constexpr static const char* py_typing_preamble = "from typing_extensions import Self";
752#else
753 constexpr static const char* py_typing_preamble = "from typing import Self";
754#endif
755};
756
757
758
759// --- booleans ------------------------------------------------------------------------------------
760
761/** @ingroup PyExportTraits
762 */
763template <>
764struct PyExportTraits<bool>
765{
766 constexpr static const char* py_typing = "bool";
767
768 LASS_PYTHON_DLL static PyObject* build(bool v);
769 LASS_PYTHON_DLL static int get(PyObject* obj, bool& v);
770};
771
772
773// --- signed integers -----------------------------------------------------------------------------
774
775/** Helper class to create PyExportTraits for signed integers
776 *
777 * An `OverflowError` will be set when trying to get a value that is out of range.
778 *
779 * @ingroup PyExportTraits
780 */
781template <typename Integer>
783{
784 constexpr static const char* py_typing = "int";
785
786 static_assert(sizeof(Integer) <= sizeof(long), "integer must fit in long");
787 static PyObject* build(Integer v)
788 {
789 return PyLong_FromLong(v);
790 }
791 static int get(PyObject* obj, Integer& v)
792 {
793#if LASS_USE_OLD_EXPORTRAITS_INT
794 if (PyLong_Check(obj))
795 {
796# if HAVE_LONG_LONG
797 const PY_LONG_LONG x = PyLong_AsLongLong(obj);
798# else
799 const long x = PyLong_AsLong(obj);
800# endif
801 if (PyErr_Occurred())
802 {
803 PyErr_Format(PyExc_TypeError, "not a %s: overflow", num::NumTraits<Integer>::name().c_str());
804 return 1;
805 }
806 return impl::pyNumCast(x, v);
807 }
808 PyErr_SetString(PyExc_TypeError, "not an integer");
809 return 1;
810#else
811# if HAVE_LONG_LONG
812 const PY_LONG_LONG x = PyLong_AsLongLong(obj);
813# else
814 const long x = PyLong_AsLong(obj);
815# endif
816 if (x == -1 && PyErr_Occurred())
817 {
818 return 1;
819 }
820 try
821 {
822 v = num::numCast<Integer>(x);
823 }
824 catch (const num::BadNumCast& err)
825 {
826 PyErr_SetString(PyExc_OverflowError, err.what());
827 return 1;
828 }
829 return 0;
830#endif
831 }
832};
833
834/** `signed char` is mapped to Python `int`
835 *
836 * An `OverflowError` will be set when trying to get a value that is out of range.
837 *
838 * @ingroup PyExportTraits
839 */
840template <>
841struct PyExportTraits<signed char>: PyExportTraitsSigned<signed char>
842{
843};
844
845/** `signed short` is mapped to Python `int`
846 *
847 * An `OverflowError` will be set when trying to get a value that is out of range.
848 *
849 * @ingroup PyExportTraits
850 */
851template <>
852struct PyExportTraits<signed short>: PyExportTraitsSigned<signed short>
853{
854};
855
856/** `signed int` is mapped to Python `int`
857 *
858 * An `OverflowError` will be set when trying to get a value that is out of range.
859 *
860 * @ingroup PyExportTraits
861 */
862template <>
863struct PyExportTraits<signed int>: PyExportTraitsSigned<signed int>
864{
865};
866
867/** `signed long` is mapped to Python `int`
868 *
869 * An `OverflowError` will be set when trying to get a value that is out of range.
870 *
871 * @ingroup PyExportTraits
872 */
873template <>
874struct PyExportTraits<signed long>: PyExportTraitsSigned<signed long>
875{
876};
877
878
879
880// --- unsigned integers ---------------------------------------------------------------------------
881
882/** Helper class to create PyExportTraits for unsigned integers
883 *
884 * An `OverflowError` will be set when trying to get a value that is out of range.
885 *
886 * @ingroup PyExportTraits
887 */
888template <typename Integer>
890{
891 constexpr static const char* py_typing = "int";
892
893 static_assert(sizeof(Integer) <= sizeof(unsigned long), "integer must fit in unsigned long");
894 static PyObject* build(Integer v)
895 {
896 return PyLong_FromUnsignedLong(v);
897 }
898 static int get(PyObject* obj, Integer& v)
899 {
900#if LASS_USE_OLD_EXPORTRAITS_INT
901 if (PyLong_Check(obj))
902 {
903# if HAVE_LONG_LONG
904 const unsigned PY_LONG_LONG x = PyLong_AsUnsignedLongLong(obj);
905# else
906 const unsigned long x = PyLong_AsUnsignedLong(obj);
907# endif
908 if (PyErr_Occurred())
909 {
910 PyErr_Format(PyExc_TypeError, "not a %s: overflow", num::NumTraits<Integer>::name().c_str());
911 return 1;
912 }
913 return impl::pyNumCast(x, v);
914 }
915 PyErr_SetString(PyExc_TypeError, "not an integer");
916 return 1;
917#else
918 if (!PyLong_Check(obj))
919 {
920 // PyLong_AsUnsignedLongLong and PyLong_AsUnsignedLong only accepts PyLong objects.
921 // They don't try to use __index__, so let's do this explicitly.
922 TPyObjPtr o(PyNumber_Index(obj));
923 if (!o)
924 {
925 // PyErr_SetString(PyExc_TypeError, "not an integer");
926 return 1;
927 }
928 return PyExportTraits<Integer>::get(o.get(), v);
929 }
930# if HAVE_LONG_LONG
931 const unsigned PY_LONG_LONG x = PyLong_AsUnsignedLongLong(obj);
932 if (x == ((unsigned PY_LONG_LONG) - 1) && PyErr_Occurred())
933 {
934 return 1;
935 }
936# else
937 const unsigned long x = PyLong_AsUnsignedLong(obj);
938 if (x == ((unsigned long) - 1) && PyErr_Occurred())
939 {
940 return 1;
941 }
942# endif
943 try
944 {
945 v = num::numCast<Integer>(x);
946 }
947 catch (const num::BadNumCast& err)
948 {
949 PyErr_SetString(PyExc_OverflowError, err.what());
950 return 1;
951 }
952 return 0;
953#endif
954 }
955};
956
957/** `unsigned char` is mapped to Python `int`
958 *
959 * An `OverflowError` will be set when trying to get a value that is out of range.
960 *
961 * @ingroup PyExportTraits
962 */
963template <>
964struct PyExportTraits<unsigned char>: PyExportTraitsUnsigned<unsigned char>
965{
966};
967
968/** `unsigned short` is mapped to Python `int`
969 *
970 * An `OverflowError` will be set when trying to get a value that is out of range.
971 *
972 * @ingroup PyExportTraits
973 */
974template <>
975struct PyExportTraits<unsigned short>: PyExportTraitsUnsigned<unsigned short>
976{
977};
978
979/** `unsigned int` is mapped to Python `int`
980 *
981 * An `OverflowError` will be set when trying to get a value that is out of range.
982 *
983 * @ingroup PyExportTraits
984 */
985template <>
986struct PyExportTraits<unsigned int>: PyExportTraitsUnsigned<unsigned int>
987{
988};
989
990/** `unsigned long` is mapped to Python `int`
991 *
992 * An `OverflowError` will be set when trying to get a value that is out of range.
993 *
994 * @ingroup PyExportTraits
995 */
996template <>
997struct PyExportTraits<unsigned long>: PyExportTraitsUnsigned<unsigned long>
998{
999};
1000
1001// --- long long -----------------------------------------------------------------------------------
1002
1003#ifdef HAVE_LONG_LONG
1004
1005/** `signed long long` is mapped to Python `int`
1006 *
1007 * An `OverflowError` will be set when trying to get a value that is out of range.
1008 *
1009 * @ingroup PyExportTraits
1010 */
1011template <>
1012struct PyExportTraits<signed PY_LONG_LONG>
1013{
1014 constexpr static const char* py_typing = "int";
1015
1016 LASS_PYTHON_DLL static PyObject* build(signed PY_LONG_LONG v);
1017 LASS_PYTHON_DLL static int get(PyObject* obj, signed PY_LONG_LONG& v);
1018};
1019
1020/** `unsigned long long` is mapped to Python `int`
1021 *
1022 * An `OverflowError` will be set when trying to get a value that is out of range.
1023 *
1024 * @ingroup PyExportTraits
1025 */
1026template <>
1027struct PyExportTraits<unsigned PY_LONG_LONG>
1028{
1029 constexpr static const char* py_typing = "int";
1030
1031 LASS_PYTHON_DLL static PyObject* build(unsigned PY_LONG_LONG v);
1032 LASS_PYTHON_DLL static int get(PyObject* obj, unsigned PY_LONG_LONG& v);
1033};
1034
1035#endif
1036
1037
1038
1039// --- floating point numbers ----------------------------------------------------------------------
1040
1041/** Helper class to create PyExportTraits for floating point numbers
1042 * @ingroup PyExportTraits
1043 */
1044template <typename Float>
1046{
1047 constexpr static const char* py_typing = "float";
1048 constexpr static const char* py_typing_param = "_Float";
1049 constexpr static const char* py_typing_preamble =
1050 "from typing import SupportsFloat, SupportsIndex\n"
1051 "type _Float = float | SupportsFloat | SupportsIndex\n";
1052
1053 static PyObject* build(Float v)
1054 {
1055 return PyFloat_FromDouble(v);
1056 }
1057 static int get(PyObject* obj, Float& v)
1058 {
1059#if LASS_USE_OLD_EXPORTRAITS_FLOAT
1060 if (PyFloat_Check(obj))
1061 {
1062 return impl::pyNumCast(PyFloat_AS_DOUBLE(obj), v);
1063 }
1064 if (PyLong_Check(obj))
1065 {
1066 const double x = PyLong_AsDouble(obj);
1067 if (PyErr_Occurred())
1068 {
1069 PyErr_Format(PyExc_TypeError, "not a %s: overflow", num::NumTraits<Float>::name().c_str());
1070 return 1;
1071 }
1072 return impl::pyNumCast(x, v);
1073 }
1074 PyErr_SetString(PyExc_TypeError, "not a float or integer");
1075 return 1;
1076#else
1077 double x;
1078 if (PyFloat_CheckExact(obj))
1079 {
1080 x = PyFloat_AS_DOUBLE(obj);
1081 }
1082 else if (PyLong_Check(obj))
1083 {
1084 x = PyLong_AsDouble(obj);
1085 if (x == -1.0 && PyErr_Occurred())
1086 {
1087 return 1;
1088 }
1089 }
1090 else
1091 {
1092 x = PyFloat_AsDouble(obj);
1093 if (x == -1.0 && PyErr_Occurred())
1094 {
1095 return 1;
1096 }
1097 }
1098 v = static_cast<Float>(x);
1099 return 0;
1100#endif
1101 }
1102};
1103
1104/** `float` is mapped to Python `float` type, which is a C `double`.
1105 *
1106 * As argument, other Python types than `float` are also accepted, as long as they can be
1107 * converted to a floating point number, i.e. `int`, and any type implementing the `__float__` or
1108 * `__index__` methods.
1109 *
1110 * @note This means that precision may be lost when converting from a Python `float` to a
1111 * C++ `float`, similar to converting from a `double` to a `float` using a static cast:
1112 * `static_cast<float>(doubleValue)`. No exception is raised in this case.
1113 *
1114 * @sa PyExportTraitsFloat
1115 * @ingroup PyExportTraits
1116 */
1117template <>
1119{
1120};
1121
1122/** `double` is mapped to Python `float` type, which is also a C `double`.
1123 *
1124 * As argument, other Python types than `float` are also accepted, as long as they can be
1125 * converted to a floating point number, i.e. `int`, and any type implementing the `__float__` or
1126 * `__index__` methods.
1127 *
1128 * @sa PyExportTraitsFloat
1129 * @ingroup PyExportTraits
1130 */
1131template <>
1133{
1134};
1135
1136/** `long double` is mapped to Python `float` type, which is a C `double`.
1137 *
1138 * As argument, other Python types than `float` are also accepted, as long as they can be
1139 * converted to a floating point number, i.e. `int`, and any type implementing the `__float__` or
1140 * `__index__` methods.
1141 *
1142 * @note This means that precision may be lost when converting from a C++ `long double` to a
1143 * Python `float`, similar to converting from a `long double` to a `double` using a static
1144 * cast: `static_cast<double>(longDoubleValue)`. No exception is raised in this case.
1145 *
1146 * @sa PyExportTraitsFloat
1147 * @ingroup PyExportTraits
1148 */
1149template <>
1150struct PyExportTraits<long double>: PyExportTraitsFloat<long double>
1151{
1152};
1153
1154
1155
1156// --- complex numbers -----------------------------------------------------------------------------
1157
1158/** `std::complex<T>` is always mapped to Python `complex` type.
1159 *
1160 * As argument, other Python types than `complex` are also accepted, as long as they can be
1161 * converted to a complex number, i.e. `int`, `float`, and any type implementing the `__complex__`,
1162 * `__float__`, or `__index__` methods.
1163 *
1164 * @note The Python `complex` type uses C `double` precision for both real and imaginary parts,
1165 * meaning that precision may be lost when converting to a `std::complex<float>` or from a
1166 * `std::complex<long double>`, similar to using `static_cast` to convert between floating
1167 * point types. No exception is raised in this case.
1168 *
1169 * @ingroup PyExportTraits
1170 */
1171template <typename T>
1172struct PyExportTraits< std::complex<T> >
1173{
1174 constexpr static const char* py_typing = "complex";
1175 constexpr static const char* py_typing_param = "_Complex";
1176 constexpr static const char* py_typing_preamble =
1177 "from typing import SupportsComplex, SupportsFloat, SupportsIndex\n"
1178 "type _Complex = complex | SupportsComplex | SupportsFloat | SupportsIndex\n";
1179
1180 static PyObject* build(const std::complex<T>& v)
1181 {
1182 return PyComplex_FromDoubles(
1183 static_cast<double>(v.real()),
1184 static_cast<double>(v.imag()));
1185 }
1186 static int get(PyObject* obj, std::complex<T>& v)
1187 {
1188#if LASS_USE_OLD_EXPORTRAITS_COMPLEX
1189 T re, im;
1190 if (PyExportTraits<T>::get(obj, re) == 0)
1191 {
1192 v = std::complex<T>(re, 0);
1193 return 0;
1194 }
1195 PyErr_Clear();
1196 if (!PyComplex_Check(obj))
1197 {
1198 PyErr_SetString(PyExc_TypeError, "not a complex number");
1199 return 1;
1200 }
1201 if (impl::pyNumCast(PyComplex_RealAsDouble(obj), re) != 0)
1202 {
1203 return 1;
1204 }
1205 if (impl::pyNumCast(PyComplex_ImagAsDouble(obj), im) != 0)
1206 {
1207 return 1;
1208 }
1209 v = std::complex<T>(re, im);
1210 return 0;
1211#else
1212 Py_complex c = PyComplex_AsCComplex(obj);
1213 if (c.real == -1.0 && PyErr_Occurred())
1214 {
1215 return 1;
1216 }
1217 v = std::complex<T>(static_cast<T>(c.real), static_cast<T>(c.imag));
1218 return 0;
1219#endif
1220 }
1221};
1222
1223
1224
1225// --- slices --------------------------------------------------------------------------------------
1226
1227/** Converts between Slice and Python slice objects
1228 *
1229 * @ingroup PyExportTraits
1230 * @sa Slice
1231 */
1232template <>
1234{
1235 static constexpr const char* py_typing = "slice";
1236
1237 LASS_PYTHON_DLL static PyObject* build(const Slice &slice);
1238 LASS_PYTHON_DLL static int get(PyObject* obj, Slice& slice);
1239};
1240
1241
1242
1243// --- strings -------------------------------------------------------------------------------------
1244
1245/** `std::basic_string_view<T>` is mapped to Python `str`
1246 *
1247 * There's no `get()` function as lifetime is not managed, but you can still use
1248 * `std::basic_string_view<T>` as function parameters as `ArgumentTraits` is specialized for it.
1249 *
1250 * @ingroup PyExportTraits
1251 */
1252template <typename T>
1253struct PyExportTraits<std::basic_string_view<T>>
1254{
1255 constexpr static const char* py_typing = "str";
1256
1257 static PyObject* build(std::basic_string_view<T> v)
1258 {
1259 return impl::buildStringImpl(v.data(), v.size());
1260 }
1261};
1262
1263
1264/** @ingroup PyExportTraits
1265 */
1266template <>
1267struct PyExportTraits<const char*>
1268{
1269 constexpr static const char* py_typing = "str | None";
1270
1271 LASS_PYTHON_DLL static PyObject* build(const char* v);
1272};
1273
1274
1275/** @ingroup PyExportTraits
1276 */
1277template <size_t N>
1278struct PyExportTraits<const char [N]>
1279{
1280 static PyObject* build(const char* v)
1281 {
1282 static_assert(N > 1, "N should include the null-terminator");
1283 return impl::buildStringImpl(v, N - 1);
1284 }
1285};
1286
1287
1288/** @ingroup PyExportTraits
1289 */
1290template <size_t N>
1291struct PyExportTraits<char [N]> : PyExportTraits<const char [N]>
1292{
1293};
1294
1295
1296/** @ingroup PyExportTraits
1297 */
1298template <>
1299struct PyExportTraits<std::string>
1300{
1301 constexpr static const char* py_typing = "str";
1302
1303 LASS_PYTHON_DLL static PyObject* build(const std::string& v);
1304 LASS_PYTHON_DLL static int get(PyObject* obj, std::string& v);
1305};
1306
1307
1308/** @ingroup PyExportTraits
1309 */
1310template <>
1311struct PyExportTraits<const wchar_t*>
1312{
1313 constexpr static const char* py_typing = "str | None";
1314
1315 LASS_PYTHON_DLL static PyObject* build(const wchar_t* v);
1316};
1317
1318
1319/** @ingroup PyExportTraits
1320 */
1321template <size_t N>
1322struct PyExportTraits<const wchar_t [N]>
1323{
1324 static PyObject* build(const wchar_t* v)
1325 {
1326 return impl::buildStringImpl(v, N);
1327 }
1328};
1329
1330
1331/** @ingroup PyExportTraits
1332 */
1333template <size_t N>
1334struct PyExportTraits<wchar_t [N]>: PyExportTraits<const wchar_t [N]>
1335{
1336};
1337
1338
1339/** @ingroup PyExportTraits
1340 */
1341template <>
1342struct PyExportTraits<std::wstring>
1343{
1344 constexpr static const char* py_typing = "str";
1345
1346 LASS_PYTHON_DLL static PyObject* build(const std::wstring& v);
1347 LASS_PYTHON_DLL static int get(PyObject* obj, std::wstring& v);
1348};
1349
1350
1351#if LASS_HAVE_STD_U8STRING
1352#if __cpp_lib_char8_t
1353
1354/** UTF-8 `const char8_t*` string is mapped to Python `str | None`, as it can be null.
1355 *
1356 * An empty string is mapped to an empty Python string, not to `None`.
1357 *
1358 * There's no `get()` function as lifetime is not managed, but you can still use `const char8_t*`
1359 * as function parameters as `ArgumentTraits` is specialized for it.
1360 *
1361 * @ingroup PyExportTraits
1362 */
1363template <>
1364struct PyExportTraits<const char8_t*>
1365{
1366 constexpr static const char* py_typing = "str | None";
1367
1368 LASS_PYTHON_DLL static PyObject* build(const char8_t* v);
1369};
1370
1371
1372/** @ingroup PyExportTraits
1373 */
1374template <size_t N>
1375struct PyExportTraits<const char8_t[N]>
1376{
1377 static PyObject* build(const char8_t* v)
1378 {
1379 return impl::buildStringImpl(v, N);
1380 }
1381};
1382
1383
1384/** @ingroup PyExportTraits
1385 */
1386template <size_t N>
1387struct PyExportTraits<char8_t[N]> : PyExportTraits<const char8_t[N]>
1388{
1389};
1390
1391
1392/** UTF-8 `std::u8string` is mapped to `str`
1393 *
1394 * @ingroup PyExportTraits
1395 */
1396template <>
1397struct PyExportTraits<std::u8string>
1398{
1399 constexpr static const char* py_typing = "str";
1400
1401 LASS_PYTHON_DLL static PyObject* build(const std::u8string& v);
1402 LASS_PYTHON_DLL static int get(PyObject* obj, std::u8string& v);
1403};
1404
1405#endif
1406#endif
1407
1408
1409/** UTF-16 `const char16_t*` string is mapped to Python `str | None`, as it can be null.
1410 *
1411 * An empty string is mapped to an empty Python string, not to `None`.
1412 *
1413 * There's no `get()` function as lifetime is not managed, but you can still use `const char16_t*`
1414 * as function parameters as `ArgumentTraits` is specialized for it.
1415 *
1416 * @ingroup PyExportTraits
1417 */
1418template <>
1419struct PyExportTraits<const char16_t*>
1420{
1421 constexpr static const char* py_typing = "str | None";
1422
1423 LASS_PYTHON_DLL static PyObject* build(const char16_t* v);
1424};
1425
1426
1427/** @ingroup PyExportTraits
1428 */
1429template <size_t N>
1430struct PyExportTraits<const char16_t[N]>
1431{
1432 static PyObject* build(const char16_t* v)
1433 {
1434 return impl::buildStringImpl(v, N);
1435 }
1436};
1437
1438
1439/** @ingroup PyExportTraits
1440 */
1441template <size_t N>
1442struct PyExportTraits<char16_t[N]> : PyExportTraits<const char16_t[N]>
1443{
1444};
1445
1446
1447/** UTF-16 `std::u16string` is mapped to `str`
1448 *
1449 * @ingroup PyExportTraits
1450 */
1451template <>
1452struct PyExportTraits<std::u16string>
1453{
1454 constexpr static const char* py_typing = "str";
1455
1456 LASS_PYTHON_DLL static PyObject* build(const std::u16string& v);
1457 LASS_PYTHON_DLL static int get(PyObject* obj, std::u16string& v);
1458};
1459
1460
1461/** UTF-32 `const char32_t*` string is mapped to Python `str | None`, as it can be null.
1462 *
1463 * An empty string is mapped to an empty Python string, not to `None`.
1464 *
1465 * There's no `get()` function as lifetime is not managed, but you can still use `const char32_t*`
1466 * as function parameters as `ArgumentTraits` is specialized for it.
1467 *
1468 * @ingroup PyExportTraits
1469 */
1470template <>
1471struct PyExportTraits<const char32_t*>
1472{
1473 constexpr static const char* py_typing = "str | None";
1474
1475 LASS_PYTHON_DLL static PyObject* build(const char32_t* v);
1476};
1477
1478
1479/** @ingroup PyExportTraits
1480 */
1481template <size_t N>
1482struct PyExportTraits<const char32_t[N]>
1483{
1484 static PyObject* build(const char32_t* v)
1485 {
1486 return impl::buildStringImpl(v, N);
1487 }
1488};
1489
1490
1491/** @ingroup PyExportTraits
1492 */
1493template <size_t N>
1494struct PyExportTraits<char32_t[N]> : PyExportTraits<const char32_t[N]>
1495{
1496};
1497
1498
1499/** UTF-32 `std::u32string` is mapped to `str`
1500 *
1501 * @ingroup PyExportTraits
1502 */
1503template <>
1504struct PyExportTraits<std::u32string>
1505{
1506 constexpr static const char* py_typing = "str";
1507
1508 LASS_PYTHON_DLL static PyObject* build(const std::u32string& v);
1509 LASS_PYTHON_DLL static int get(PyObject* obj, std::u32string& v);
1510};
1511
1512
1513}
1514}
1515
1516#endif
1517
1518// EOF
Wrapper to type-hint return values in Python that maybe None but not likely.
Definition maybe_none.h:82
Wrapper to prevent None values being passed to and from Python.
Definition no_none.h:84
Wrapper to type-hint a return value as Self.
Definition self.h:92
PyObjectPtr< PyObject >::Type TPyObjPtr
PyObjectPtr to a PyObject.
PyObject * fromSharedPtrToNakedCast(const util::SharedPtr< T, PyObjectStorage, PyObjectCounter > &object)
fromSharedPtrToNakedCast.
lass::util::SharedPtr< T, PyObjectStorage, PyObjectCounter > fromNakedToSharedPtrCast(PyObject *object)
fromNakedToSharedPtrCast.
ColorRGBA out(const ColorRGBA &a, const ColorRGBA &b)
a held out by b, part of a outside b.
Comprehensive C++ to Python binding library.
general utility, debug facilities, ...
Library for Assembled Shared Sources.
Definition config.h:53
Helper class to create PyExportTraits for floating point numbers.
Helper class to create PyExportTraits for MaybeNone wrapped types.
Helper class to create PyExportTraits for NoNone wrapped types.
static PyObject * build(const NoNone< T > &value)
Raise a Python TypeError if value is equal to nullptr
static int get(PyObject *obj, NoNone< T > &value)
Raise a Python TypeError if obj is equal to None
Helper class to create PyExportTraits for signed integers.
Helper class to create PyExportTraits for unsigned integers.
by copy, general case assumes shadow type or PyObjectPlus based type.
Helper type to get or return Python slice objects.
Definition subscript.h:193