Library of Assembled Shared Sources
export_traits.cpp
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#include "python_common.h"
44#include "export_traits.h"
46
47namespace
48{
49
50template <typename string_type>
51int getStringImpl(PyObject* obj, string_type& v)
52{
53 typedef typename string_type::value_type value_type;
54 static_assert(sizeof(value_type) == 1, "string_type should be narrow string");
55 if (!PyUnicode_Check(obj))
56 {
57 PyErr_SetString(PyExc_TypeError, "not a string");
58 return 1;
59 }
60 Py_ssize_t size;
61 const char* data = PyUnicode_AsUTF8AndSize(obj, &size);
62 if (!data)
63 {
64 return 1;
65 }
66 v = string_type(reinterpret_cast<const value_type*>(data), static_cast<size_t>(size));
67 return 0;
68}
69
70template <typename string_type>
71int getWideStringImpl(PyObject * obj, string_type& v)
72{
73 typedef typename string_type::value_type value_type;
74 static_assert(sizeof(value_type) == sizeof(wchar_t), "string_type should be wide string");
75 if (!PyUnicode_Check(obj))
76 {
77 PyErr_SetString(PyExc_TypeError, "not a string");
78 return 1;
79 }
80 Py_ssize_t n = PyUnicode_AsWideChar(obj, 0, 0); // takes care of UTF-16 and UTF-32 conversions
81 if (n == -1)
82 {
83 return 1;
84 }
85 const Py_ssize_t size = n - 1; // n includes terminating null character.
86 if (size <= 0)
87 {
88 v = string_type();
89 return 0;
90 }
91 string_type tmp(static_cast<size_t>(size), '\0');
92 if (PyUnicode_AsWideChar(obj, reinterpret_cast<wchar_t*>(&tmp[0]), size) == -1)
93 {
94 return 1;
95 }
96 v = std::move(tmp);
97 return 0;
98}
99
100}
101
102
103namespace lass
104{
105namespace python
106{
107namespace impl
108{
109
110PyObject* buildStringImpl(const char* v, size_t size)
111{
112 if (!v)
113 {
114 Py_RETURN_NONE;
115 }
116 if (size > PY_SSIZE_T_MAX)
117 {
118 PyErr_SetString(PyExc_OverflowError, "input too long");
119 return 0;
120 }
121 return PyUnicode_DecodeUTF8(v, static_cast<Py_ssize_t>(size), 0);
122}
123
124
125PyObject* buildStringImpl(const wchar_t* v, size_t size)
126{
127 if (!v)
128 {
129 Py_RETURN_NONE;
130 }
131 if (size > PY_SSIZE_T_MAX)
132 {
133 PyErr_SetString(PyExc_OverflowError, "input too long");
134 return 0;
135 }
136 return PyUnicode_FromWideChar(v, static_cast<Py_ssize_t>(size));
137}
138
139
140#if LASS_HAVE_STD_U8STRING
141
142PyObject* buildStringImpl(const char8_t* v, size_t size)
143{
144 return buildStringImpl(reinterpret_cast<const char*>(v), size);
145}
146
147#endif
148
149
150PyObject* buildStringImpl(const char16_t* v, size_t size)
151{
152 if (!v)
153 {
154 Py_RETURN_NONE;
155 }
156 if (size > (PY_SSIZE_T_MAX / 2))
157 {
158 PyErr_SetString(PyExc_OverflowError, "input too long");
159 return 0;
160 }
161 return PyUnicode_DecodeUTF16(reinterpret_cast<const char*>(v), static_cast<Py_ssize_t>(size * 2), nullptr, nullptr);
162}
163
164
165PyObject* buildStringImpl(const char32_t* v, size_t size)
166{
167 if (!v)
168 {
169 Py_RETURN_NONE;
170 }
171 if (size > (PY_SSIZE_T_MAX / 4))
172 {
173 PyErr_SetString(PyExc_OverflowError, "input too long");
174 return 0;
175 }
176 return PyUnicode_DecodeUTF32(reinterpret_cast<const char*>(v), static_cast<Py_ssize_t>(size * 4), nullptr, nullptr);
177}
178
179}
180
181
182
183PyObject* PyExportTraits<void*>::build(void* value)
184{
185 if (!value)
186 {
187 Py_RETURN_NONE;
188 }
189 return PyCapsule_New(value, 0, 0);
190}
191
192
193int PyExportTraits<void*>::get(PyObject* obj, void*& value)
194{
195 if (obj == Py_None)
196 {
197 value = 0;
198 return 0;
199 }
200 if (!PyCapsule_CheckExact(obj))
201 {
202 PyErr_SetString(PyExc_TypeError, "does not evaluate to a void*");
203 return 1;
204 }
205 void* v = PyCapsule_GetPointer(obj, 0);
206 if (!v)
207 {
208 return 1;
209 }
210 value = v;
211 return 0;
212}
213
214
215
216PyObject* PyExportTraits<std::nullptr_t>::build(std::nullptr_t /* value */)
217{
218 Py_RETURN_NONE;
219}
220
221
222int PyExportTraits<std::nullptr_t>::get(PyObject* obj, std::nullptr_t& value)
223{
224 if (obj != Py_None)
225 {
226 PyErr_SetString(PyExc_TypeError, "must be None");
227 return 1;
228 }
229 value = nullptr;
230 return 0;
231}
232
233
234
235PyObject* PyExportTraits<bool>::build(bool v)
236{
237 if (v)
238 {
239 Py_RETURN_TRUE;
240 }
241 else
242 {
243 Py_RETURN_FALSE;
244 }
245}
246
247
248int PyExportTraits<bool>::get(PyObject* obj, bool& v)
249{
250 int result = PyObject_IsTrue(obj);
251 if (result == -1)
252 {
253 PyErr_SetString(PyExc_TypeError, "does not evaluate to a boolean");
254 return 1;
255 }
256 v = (result != 0);
257 return 0;
258}
259
260
261#ifdef HAVE_LONG_LONG
262
263PyObject* PyExportTraits<signed PY_LONG_LONG>::build(signed PY_LONG_LONG v)
264{
265 return PyLong_FromLongLong(v);
266}
267
268
269int PyExportTraits<signed PY_LONG_LONG>::get(PyObject* obj, signed PY_LONG_LONG& v)
270{
271#if LASS_USE_OLD_EXPORTRAITS_INT
272 if (PyLong_Check(obj))
273 {
274 v = PyLong_AsLongLong(obj);
275 return 0;
276 }
277 PyErr_SetString(PyExc_TypeError, "not an integer");
278 return 1;
279#else
280# if PY_VERSION_HEX < 0x030a0000 // < 3.10
281 // PyLong_AsLongLong also accept __int__, so floats are ints too :-(
282 // From 3.10 onwards, __int__ is no longer accepted, only __index__.
283 // Let's do the same for Python < 3.10.
284 if (!PyLong_Check(obj))
285 {
286 TPyObjPtr o(PyNumber_Index(obj));
287 if (!o)
288 {
289 // PyErr_SetString(PyExc_TypeError, "not an integer");
290 return 1;
291 }
292 return PyExportTraits<signed PY_LONG_LONG>::get(o.get(), v);
293 }
294# endif
295 v = PyLong_AsLongLong(obj);
296 if (v == -1 && PyErr_Occurred())
297 {
298 return 1;
299 }
300 return 0;
301#endif
302}
303
304
305PyObject* PyExportTraits<unsigned PY_LONG_LONG>::build(unsigned PY_LONG_LONG v)
306{
307 return PyLong_FromUnsignedLongLong(v);
308}
309
310
311int PyExportTraits<unsigned PY_LONG_LONG>::get(PyObject* obj, unsigned PY_LONG_LONG& v)
312{
313#if LASS_USE_OLD_EXPORTRAITS_INT
314 if (PyLong_Check(obj))
315 {
316 v = PyLong_AsUnsignedLongLong(obj);
317 return 0;
318 }
319 PyErr_SetString(PyExc_TypeError, "not an integer");
320 return 1;
321#else
322 if (!PyLong_Check(obj))
323 {
324 // PyLong_AsUnsignedLongLong only accepts PyLong objects.
325 // It doesn't try to use __index__, so let's do this explicitly.
326 TPyObjPtr o(PyNumber_Index(obj));
327 if (!o)
328 {
329 // PyErr_SetString(PyExc_TypeError, "not an integer");
330 return 1;
331 }
332 return PyExportTraits<unsigned PY_LONG_LONG>::get(o.get(), v);
333 }
334 v = PyLong_AsUnsignedLongLong(obj);
335 if (v == ((unsigned PY_LONG_LONG)-1) && PyErr_Occurred())
336 {
337 return 1;
338 }
339 return 0;
340#endif
341}
342
343#endif
344
345
346PyObject* PyExportTraits<const char*>::build(const char* v)
347{
348 if (!v)
349 {
350 Py_RETURN_NONE;
351 }
352 return PyExportTraits<std::string_view>::build(v);
353}
354
355
356PyObject* PyExportTraits<std::string>::build(const std::string& v)
357{
358 return impl::buildStringImpl(v.data(), v.length());
359}
360
361
362int PyExportTraits<std::string>::get(PyObject* obj, std::string& v)
363{
364 return getStringImpl(obj, v);
365}
366
367
368PyObject* PyExportTraits<const wchar_t*>::build(const wchar_t* v)
369{
370 if (!v)
371 {
372 Py_RETURN_NONE;
373 }
374 return PyExportTraits<std::wstring_view>::build(v);
375}
376
377
378PyObject* PyExportTraits<std::wstring>::build(const std::wstring& v)
379{
380 return impl::buildStringImpl(v.data(), v.length());
381}
382
383
384int PyExportTraits<std::wstring>::get(PyObject* obj, std::wstring& v)
385{
386 return getWideStringImpl(obj, v);
387}
388
389
390#if LASS_HAVE_STD_U8STRING
391
392PyObject* PyExportTraits<const char8_t*>::build(const char8_t* v)
393{
394 if (!v)
395 {
396 Py_RETURN_NONE;
397 }
398 return PyExportTraits<std::u8string_view>::build(v);
399}
400
401
402PyObject* PyExportTraits<std::u8string>::build(const std::u8string& v)
403{
404 return impl::buildStringImpl(reinterpret_cast<const char*>(v.data()), v.length());
405}
406
407
408int PyExportTraits<std::u8string>::get(PyObject* obj, std::u8string& v)
409{
410 return getStringImpl(obj, v);
411}
412
413#endif
414
415
416PyObject* PyExportTraits<const char16_t*>::build(const char16_t* v)
417{
418 if (!v)
419 {
420 Py_RETURN_NONE;
421 }
422 return PyExportTraits<std::u16string_view>::build(v);
423}
424
425
426PyObject* PyExportTraits<std::u16string>::build(const std::u16string& v)
427{
428 return impl::buildStringImpl(v.data(), v.length());
429}
430
431
432int PyExportTraits<std::u16string>::get(PyObject* obj, std::u16string& v)
433{
434#if LASS_HAVE_WCHAR_T == 2
435 return getWideStringImpl(obj, v);
436#else
437 if (!PyUnicode_Check(obj))
438 {
439 PyErr_SetString(PyExc_TypeError, "not a string");
440 return 1;
441 }
442 if (PyUnicode_READY(obj) != 0)
443 {
444 return 1;
445 }
446 if (PyUnicode_KIND(obj) == PyUnicode_2BYTE_KIND)
447 {
448 // all code points < 65536 are represented by a single 2-byte code unit in UTF-16.
449 v = std::u16string(reinterpret_cast<char16_t*>(PyUnicode_2BYTE_DATA(obj)), static_cast<size_t>(PyUnicode_GET_LENGTH(obj)));
450 return 0;
451 }
452 TPyObjPtr bytes(PyUnicode_AsUTF16String(obj));
453 if (!bytes)
454 {
455 return 1;
456 }
457 LASS_ASSERT(PyBytes_Check(bytes.get()));
458 const Py_ssize_t n = PyBytes_Size(bytes.get());
459 const char16_t* data = reinterpret_cast<char16_t* >(PyBytes_AsString(bytes.get()));
460 // according to PyUnicode_AsUTF16String, data must always start with a BOM for native byteorder
461 LASS_ASSERT(data[0] == 0xfeff);
462 v = std::u16string(data + 1, static_cast<size_t>((n / 2) - 1));
463 return 0;
464#endif
465}
466
467
468PyObject* PyExportTraits<const char32_t*>::build(const char32_t* v)
469{
470 if (!v)
471 {
472 Py_RETURN_NONE;
473 }
474 return PyExportTraits<std::u32string_view>::build(v);
475}
476
477
478PyObject* PyExportTraits<std::u32string>::build(const std::u32string& v)
479{
480 return impl::buildStringImpl(v.data(), v.length());
481}
482
483
484int PyExportTraits<std::u32string>::get(PyObject* obj, std::u32string& v)
485{
486#if LASS_HAVE_WCHAR_T == 4
487 return getWideStringImpl(obj, v);
488#else
489 if (!PyUnicode_Check(obj))
490 {
491 PyErr_SetString(PyExc_TypeError, "not a string");
492 return 1;
493 }
494 Py_ssize_t size = PyUnicode_GetLength(obj);
495 if (size <= 0)
496 {
497 v = std::u32string();
498 return 0;
499 }
500 std::u32string tmp(static_cast<size_t>(size), '\0');
501 if (PyUnicode_AsUCS4(obj, reinterpret_cast<Py_UCS4*>(&tmp[0]), size, 0) == nullptr)
502 {
503 return 1;
504 }
505 v = std::move(tmp);
506 return 0;
507#endif
508}
509
510
511}
512}
PyObjectPtr< PyObject >::Type TPyObjPtr
PyObjectPtr to a PyObject.
Comprehensive C++ to Python binding library.
Library for Assembled Shared Sources.
Definition config.h:53