diff options
Diffstat (limited to 'sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp')
| -rw-r--r-- | sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp b/sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp new file mode 100644 index 000000000..1f92224f5 --- /dev/null +++ b/sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp @@ -0,0 +1,158 @@ +// Copyright (C) 2025 Ford Motor Company +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "pysidedynamicenum_p.h" +#include "pysidedynamiccommon_p.h" + +#include <autodecref.h> +#include <sbkconverter.h> +#include <sbkenum.h> + +#include <QtCore/qmetaobject.h> + +using namespace Shiboken; + +// Remote Objects transfer enums as integers of the underlying type. +#define CREATE_ENUM_CONVERSION_FUNCTIONS(SUFFIX, INT_TYPE, PY_TYPE) \ +static void pythonToCpp_PyEnum_QEnum_##SUFFIX(PyObject *pyIn, void *cppOut) \ +{ \ + Enum::EnumValueType value = Enum::getValue(pyIn); \ + INT_TYPE val(value); \ + *reinterpret_cast<INT_TYPE *>(cppOut) = val; \ +} \ +static PythonToCppFunc is_PyEnum_PythonToCpp_QEnum_##SUFFIX##_Convertible(PyObject *pyIn) \ +{ \ + if (Enum::check(pyIn)) \ + return pythonToCpp_PyEnum_QEnum_##SUFFIX; \ + return {}; \ +} \ +static PyObject *cppToPython_QEnum_##SUFFIX##_PyEnum(const void *cppIn) \ +{ \ + auto convertedCppIn = *reinterpret_cast<const INT_TYPE *>(cppIn); \ + return PY_TYPE(convertedCppIn); \ +} + +CREATE_ENUM_CONVERSION_FUNCTIONS(I8, int8_t, PyLong_FromLong) +CREATE_ENUM_CONVERSION_FUNCTIONS(I16, int16_t, PyLong_FromLong) +CREATE_ENUM_CONVERSION_FUNCTIONS(I32, int32_t, PyLong_FromLong) +CREATE_ENUM_CONVERSION_FUNCTIONS(U8, uint8_t, PyLong_FromUnsignedLong) +CREATE_ENUM_CONVERSION_FUNCTIONS(U16, uint16_t, PyLong_FromUnsignedLong) +CREATE_ENUM_CONVERSION_FUNCTIONS(U32, uint32_t, PyLong_FromUnsignedLong) +CREATE_ENUM_CONVERSION_FUNCTIONS(I64, int64_t, PyLong_FromLongLong) +CREATE_ENUM_CONVERSION_FUNCTIONS(U64, uint64_t, PyLong_FromUnsignedLongLong) + +PyTypeObject *createEnumType(QMetaEnum *metaEnum) +{ + static const auto namePrefix = QByteArrayLiteral("2:PySide6.QtRemoteObjects.DynamicEnum."); + auto fullName = namePrefix + metaEnum->scope() + "." + metaEnum->enumName(); + + AutoDecRef args(PyList_New(0)); + auto *pyEnumItems = args.object(); + auto metaType = metaEnum->metaType(); + auto underlyingType = metaType.underlyingType(); + bool isUnsigned = underlyingType.flags().testFlag(QMetaType::IsUnsignedEnumeration); + for (int idx = 0; idx < metaEnum->keyCount(); ++idx) { + auto *key = PyUnicode_FromString(metaEnum->key(idx)); + auto *key_value = PyTuple_New(2); + PyTuple_SetItem(key_value, 0, key); + // Value should only return a nullopt if there is no metaObject or the index is not valid + auto valueOpt = metaEnum->value64(idx); + if (!valueOpt) { + PyErr_SetString(PyExc_RuntimeError, "Failed to get value64 from enum"); + return nullptr; + } + if (isUnsigned) { + auto *value = PyLong_FromUnsignedLongLong(*valueOpt); + PyTuple_SetItem(key_value, 1, value); + } else { + auto *value = PyLong_FromLongLong(*valueOpt); + PyTuple_SetItem(key_value, 1, value); + } + PyList_Append(pyEnumItems, key_value); + } + + PyTypeObject *newType{}; + if (metaEnum->isFlag()) + newType = Enum::createPythonEnum(fullName.constData(), pyEnumItems, "Flag"); + else + newType = Enum::createPythonEnum(fullName.constData(), pyEnumItems); + + SbkConverter *converter = nullptr; + switch (underlyingType.sizeOf()) { + case 1: + if (isUnsigned) { + converter = Conversions::createConverter(newType, + cppToPython_QEnum_U8_PyEnum); + Conversions::addPythonToCppValueConversion(converter, + pythonToCpp_PyEnum_QEnum_U8, + is_PyEnum_PythonToCpp_QEnum_U8_Convertible); + } else { + converter = Conversions::createConverter(newType, + cppToPython_QEnum_I8_PyEnum); + Conversions::addPythonToCppValueConversion(converter, + pythonToCpp_PyEnum_QEnum_I8, + is_PyEnum_PythonToCpp_QEnum_I8_Convertible); + } + break; + case 2: + if (isUnsigned) { + converter = Conversions::createConverter(newType, + cppToPython_QEnum_U16_PyEnum); + Conversions::addPythonToCppValueConversion(converter, + pythonToCpp_PyEnum_QEnum_U16, + is_PyEnum_PythonToCpp_QEnum_U16_Convertible); + } else { + converter = Conversions::createConverter(newType, + cppToPython_QEnum_I16_PyEnum); + Conversions::addPythonToCppValueConversion(converter, + pythonToCpp_PyEnum_QEnum_I16, + is_PyEnum_PythonToCpp_QEnum_I16_Convertible); + } + break; + case 4: + if (isUnsigned) { + converter = Conversions::createConverter(newType, + cppToPython_QEnum_U32_PyEnum); + Conversions::addPythonToCppValueConversion(converter, + pythonToCpp_PyEnum_QEnum_U32, + is_PyEnum_PythonToCpp_QEnum_U32_Convertible); + } else { + converter = Conversions::createConverter(newType, + cppToPython_QEnum_I32_PyEnum); + Conversions::addPythonToCppValueConversion(converter, + pythonToCpp_PyEnum_QEnum_I32, + is_PyEnum_PythonToCpp_QEnum_I32_Convertible); + } + break; + case 8: + if (isUnsigned) { + converter = Conversions::createConverter(newType, + cppToPython_QEnum_U64_PyEnum); + Conversions::addPythonToCppValueConversion(converter, + pythonToCpp_PyEnum_QEnum_U64, + is_PyEnum_PythonToCpp_QEnum_U64_Convertible); + } else { + converter = Conversions::createConverter(newType, + cppToPython_QEnum_I64_PyEnum); + Conversions::addPythonToCppValueConversion(converter, + pythonToCpp_PyEnum_QEnum_I64, + is_PyEnum_PythonToCpp_QEnum_I64_Convertible); + } + break; + default: + PyErr_SetString(PyExc_RuntimeError, "Unsupported enum underlying type"); + return nullptr; + } + auto scopedName = QByteArray(metaEnum->scope()) + "::" + metaEnum->enumName(); + Conversions::registerConverterName(converter, scopedName.constData()); + Conversions::registerConverterName(converter, metaEnum->enumName()); + // createConverter increases the ref count of type, but that will create a + // circular reference when we add the capsule with the converter's pointer + // to the type's attributes. So we need to decrease the ref count on the + // type after calling createConverter. + Py_DECREF(newType); + if (set_cleanup_capsule_attr_for_pointer(newType, "_converter_capsule", converter) < 0) + return nullptr; + + return newType; +} |
