diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/corelib/kernel/qtmochelpers.h | 354 |
1 files changed, 352 insertions, 2 deletions
diff --git a/src/corelib/kernel/qtmochelpers.h b/src/corelib/kernel/qtmochelpers.h index 9d75177645e..e5454fbf504 100644 --- a/src/corelib/kernel/qtmochelpers.h +++ b/src/corelib/kernel/qtmochelpers.h @@ -15,9 +15,11 @@ // We mean it. // -#include <QtCore/qglobal.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qtmocconstants.h> -#include <algorithm> // std::min +#include <QtCore/q20algorithm.h> // std::min, std::copy_n +#include <QtCore/q23type_traits.h> // std::is_scoped_enum #include <limits> #if 0 @@ -78,6 +80,354 @@ template <uint... Nx> constexpr auto stringData(const char (&...strings)[Nx]) # define QT_MOC_HAS_STRINGDATA 1 +struct NoType {}; + +namespace detail { +template <uint H, uint P> struct UintDataBlock +{ + static constexpr uint headerSize() { return H; } + static constexpr uint payloadSize() { return P; } + uint header[H ? H : 1] = {}; + uint payload[P ? P : 1] = {}; +}; + +template <int Idx, typename T> struct UintDataEntry +{ + T entry; + constexpr UintDataEntry(T &&entry_) : entry(std::move(entry_)) {} +}; + +// This storage type is designed similar to libc++'s std::tuple, in that it +// derives from a type unique to each of the types in the template parameter +// pack (even if they are the same type). That way, we can refer to each of +// entries uniquely by just casting *this to that unique type. +// +// Testing reveals this to compile MUCH faster than recursive approaches and +// avoids compiler constexpr-time limits. +template <typename Idx, typename... T> struct UintDataStorage; +template <int... Idx, typename... T> struct UintDataStorage<std::integer_sequence<int, Idx...>, T...> + : UintDataEntry<Idx, T>... +{ + constexpr UintDataStorage(T &&... data) + : UintDataEntry<Idx, T>(std::move(data))... + {} + + template <typename F> constexpr void forEach(F &&f) const + { + [[maybe_unused]] auto invoke = [&f](const auto &entry) { f(entry.entry); return 0; }; + int dummy[] = { + 0, + invoke(static_cast<const UintDataEntry<Idx, T> &>(*this))... + }; + (void) dummy; + } +}; +} // namespace detail + +template <typename... Block> struct UintData +{ + constexpr UintData(Block &&... data_) + : data(std::move(data_)...) + {} + + static constexpr uint count() { return sizeof...(Block); } + static constexpr uint headerSize() + { + // same as: + // return (0 + ... + Block::headerSize()); + // but not using the fold expression to avoid exceeding compiler limits + // (calculation done using int to get compile-time overflow checking) + int total = 0; + int sizes[] = { 0, Block::headerSize()... }; + for (int n : sizes) + total += n; + return total; + } + static constexpr uint payloadSize() + { + // ditto + int total = 0; + int sizes[] = { 0, Block::payloadSize()... }; + for (int n : sizes) + total += n; + return total; + } + static constexpr uint dataSize() { return headerSize() + payloadSize(); } + + template <typename Result> + constexpr void copyTo(Result &result, size_t dataoffset) const + { + uint *ptr = result.data.data(); + size_t payloadoffset = dataoffset + headerSize(); + data.forEach([&](const auto &input) { + // copy the uint data + q20::copy_n(input.header, input.headerSize(), ptr + dataoffset); + q20::copy_n(input.payload, input.payloadSize(), ptr + payloadoffset); + input.adjustOffset(ptr, uint(dataoffset), uint(payloadoffset)); + + dataoffset += input.headerSize(); + payloadoffset += input.payloadSize(); + }); + } + + template <typename F> constexpr void forEach(F &&f) const + { + data.forEach(std::forward<F>(f)); + } + +private: + detail::UintDataStorage<std::make_integer_sequence<int, count()>, Block...> data; +}; + +template <int N> struct ClassInfos : detail::UintDataBlock<2 * N, 0> +{ + constexpr ClassInfos() = default; + constexpr ClassInfos(const std::array<uint, 2> (&infos)[N]) + { + uint *out = this->header; + for (int i = 0; i < N; ++i) { + *out++ = infos[i][0]; + *out++ = infos[i][1]; + } + } +}; + +struct PropertyData : detail::UintDataBlock<5, 0> +{ + constexpr PropertyData(uint nameIndex, uint typeIndex, uint flags, uint notifyId = uint(-1), uint revision = 0) + { + this->header[0] = nameIndex; + this->header[1] = typeIndex; + this->header[2] = flags; + this->header[3] = notifyId; + this->header[4] = revision; + } + + static constexpr void adjustOffset(uint *, uint, uint) noexcept {} +}; + +template <typename Enum, int N = 0> +struct EnumData : detail::UintDataBlock<5, 2 * N> +{ +private: + static_assert(sizeof(Enum) <= sizeof(uint), "Cannot store enumeration of this size"); + template <typename T> struct RealEnum { using Type = T; }; + template <typename T> struct RealEnum<QFlags<T>> { using Type = T; }; +public: + struct EnumEntry { + int nameIndex; + typename RealEnum<Enum>::Type value; + }; + + + constexpr EnumData(uint nameOffset, uint aliasOffset, uint flags) + { + this->header[0] = nameOffset; + this->header[1] = aliasOffset; + this->header[2] = flags; + this->header[3] = N; + this->header[4] = 0; // will be set in adjustOffsets() + + if (nameOffset != aliasOffset || QtPrivate::IsQFlags<Enum>::value) + this->header[2] |= QtMocConstants::EnumIsFlag; + if constexpr (q23::is_scoped_enum_v<Enum>) + this->header[2] |= QtMocConstants::EnumIsScoped; + } + + template <int Added> constexpr auto add(const EnumEntry (&entries)[Added]) + { + EnumData<Enum, N + Added> result(this->header[0], this->header[1], this->header[2]); + + q20::copy_n(this->payload, this->payloadSize(), result.payload); + uint o = this->payloadSize(); + for (auto entry : entries) { + result.payload[o++] = uint(entry.nameIndex); + auto value = qToUnderlying(entry.value); + result.payload[o++] = uint(value); + } + return result; + } + + static constexpr void adjustOffset(uint *ptr, uint dataoffset, uint payloadoffset) noexcept + { + ptr[dataoffset + 4] += uint(payloadoffset); + } +}; + +template <typename F, uint ExtraFlags> struct FunctionData; +template <typename Ret, typename... Args, uint ExtraFlags> +struct FunctionData<Ret (Args...), ExtraFlags> + : detail::UintDataBlock<6, 2 * sizeof...(Args) + 1 + (ExtraFlags & QtMocConstants::MethodRevisioned ? 1 : 0)> +{ + static constexpr bool IsRevisioned = (ExtraFlags & QtMocConstants::MethodRevisioned) != 0; + struct FunctionParameter { + uint typeIdx; // or static meta type ID + uint nameIdx; + }; + using ParametersArray = std::array<FunctionParameter, sizeof...(Args)>; + + static constexpr void adjustOffset(uint *ptr, uint dataoffset, uint payloadoffset) noexcept + { + if constexpr (IsRevisioned) + ++payloadoffset; + ptr[dataoffset + 2] += uint(payloadoffset); + } + + constexpr + FunctionData(uint nameIndex, uint tagIndex, uint metaTypesIndex, uint flags, + uint returnType, ParametersArray params = {}) + { + this->header[0] = nameIndex; + this->header[1] = sizeof...(Args); + this->header[2] = 0; // will be set in adjustOffsets() + this->header[3] = tagIndex; + this->header[4] = flags | ExtraFlags; + this->header[5] = metaTypesIndex; + + uint *p = this->payload; + if constexpr (ExtraFlags & QtMocConstants::MethodRevisioned) + ++p; + *p++ = returnType; + for (uint i = 0; i < sizeof...(Args); ++i) + *p++ = params[i].typeIdx; + for (uint i = 0; i < sizeof...(Args); ++i) + *p++ = params[i].nameIdx; + } + + constexpr + FunctionData(uint nameIndex, uint tagIndex, uint metaTypesIndex, uint flags, uint revision, + uint returnType, ParametersArray params = {}) +#ifdef __cpp_concepts + requires(IsRevisioned) +#endif + : FunctionData(nameIndex, tagIndex, metaTypesIndex, flags, returnType, params) + { + // note: we place the revision differently from meta object revision 12 + this->payload[0] = revision; + } +}; +template <typename Ret, typename... Args, uint ExtraFlags> +struct FunctionData<Ret (Args...) const, ExtraFlags> + : FunctionData<Ret (Args...), ExtraFlags | QtMocConstants::MethodIsConst> +{ + using FunctionData<Ret (Args...), ExtraFlags | QtMocConstants::MethodIsConst>::FunctionData; +}; + +template <typename F> struct MethodData : FunctionData<F, QtMocConstants::MethodMethod> +{ + using FunctionData<F, QtMocConstants::MethodMethod>::FunctionData; +}; + +template <typename F> struct SignalData : FunctionData<F, QtMocConstants::MethodSignal> +{ + using FunctionData<F, QtMocConstants::MethodSignal>::FunctionData; +}; + +template <typename F> struct SlotData : FunctionData<F, QtMocConstants::MethodSlot> +{ + using FunctionData<F, QtMocConstants::MethodSlot>::FunctionData; +}; + +template <typename F> struct ConstructorData : FunctionData<F, QtMocConstants::MethodConstructor> +{ + using FunctionData<F, QtMocConstants::MethodConstructor>::FunctionData; +}; + +template <typename F> struct RevisionedMethodData : + FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodMethod> +{ + using FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodMethod>::FunctionData; +}; + +template <typename F> struct RevisionedSignalData : + FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodSignal> +{ + using FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodSignal>::FunctionData; +}; + +template <typename F> struct RevisionedSlotData : + FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodSlot> +{ + using FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodSlot>::FunctionData; +}; + +template <typename F> struct RevisionedConstructorData : + FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodConstructor> +{ + using FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodConstructor>::FunctionData; +}; + + + +template <uint N> struct UintDataResult +{ + std::array<uint, N> data; +}; + +template <typename Methods, typename Properties, typename Enums, + typename Constructors = UintData<>, typename ClassInfo = detail::UintDataBlock<0, 0>> +constexpr auto metaObjectData(uint flags, const Methods &methods, const Properties &properties, + const Enums &enums, const Constructors &constructors = {}, + const ClassInfo &classInfo = {}) +{ + constexpr uint HeaderSize = 14; + constexpr uint TotalSize = HeaderSize + + Properties::dataSize() + + Enums::dataSize() + + Methods::dataSize() + + Constructors::dataSize() + + ClassInfo::headerSize() // + ClassInfo::payloadSize() + + 1; // empty EOD + UintDataResult<TotalSize> result = {}; + uint dataoffset = HeaderSize; + + result.data[0] = QtMocConstants::OutputRevision; + result.data[1] = 0; // class name index (it's always 0) + + result.data[2] = ClassInfo::headerSize() / 2; + result.data[3] = ClassInfo::headerSize() ? dataoffset : 0; + q20::copy_n(classInfo.header, classInfo.headerSize(), result.data.data() + dataoffset); + dataoffset += ClassInfo::headerSize(); + + result.data[6] = properties.count(); + result.data[7] = properties.count() ? dataoffset : 0; + properties.copyTo(result, dataoffset); + dataoffset += properties.dataSize(); + + result.data[8] = enums.count(); + result.data[9] = enums.count() ? dataoffset : 0; + enums.copyTo(result, dataoffset); + dataoffset += enums.dataSize(); + + result.data[4] = methods.count(); + result.data[5] = methods.count() ? dataoffset : 0; + methods.copyTo(result, dataoffset); + dataoffset += methods.dataSize(); + + result.data[10] = constructors.count(); + result.data[11] = constructors.count() ? dataoffset : 0; + constructors.copyTo(result, dataoffset); + dataoffset += constructors.dataSize(); + + result.data[12] = flags; + + // count the number of signals + if constexpr (Methods::count()) { + constexpr uint MethodHeaderSize = Methods::headerSize() / Methods::count(); + const uint *ptr = &result.data[result.data[5]]; + const uint *end = &result.data[result.data[5] + MethodHeaderSize * Methods::count()]; + for ( ; ptr < end; ptr += MethodHeaderSize) { + if ((ptr[4] & QtMocConstants::MethodSignal) == 0) + break; + ++result.data[13]; + } + } + + return result; +} + +#define QT_MOC_HAS_UINTDATA 1 + } // namespace QtMocHelpers QT_END_NAMESPACE |
