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