aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp
blob: 1f92224f52cb3fb69a441a3405719c8e5e1294f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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;
}