Revision control
Copy as Markdown
// Copyright (C) 2022 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTMOCHELPERS_H
#define QTMOCHELPERS_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists to be used by the code that
// moc generates. This file will not change quickly, but it over the long term,
// it will likely change or even be removed.
//
// We mean it.
//
#include <QtCore/qmetatype.h>
#include <QtCore/qtmocconstants.h>
#include <QtCore/q20algorithm.h> // std::min, std::copy_n
#include <QtCore/q23type_traits.h> // std::is_scoped_enum
#include <limits>
#if 0
#pragma qt_no_master_include
#endif
QT_BEGIN_NAMESPACE
namespace QtMocHelpers {
// The maximum Size of a string literal is 2 GB on 32-bit and 4 GB on 64-bit
// (but the compiler is likely to give up before you get anywhere near that much)
static constexpr size_t MaxStringSize =
(std::min)(size_t((std::numeric_limits<uint>::max)()),
size_t((std::numeric_limits<qsizetype>::max)()));
template <uint UCount, uint SCount, size_t SSize, uint MCount> struct MetaObjectContents
{
struct StaticContent {
uint data[UCount];
uint stringdata[SCount];
char strings[SSize];
} staticData = {};
struct RelocatingContent {
const QtPrivate::QMetaTypeInterface *metaTypes[MCount];
} relocatingData = {};
};
template <int Count, size_t StringSize> struct StringData
{
static_assert(StringSize <= MaxStringSize, "Meta Object data is too big");
uint offsetsAndSizes[Count] = {};
char stringdata0[StringSize] = {};
constexpr StringData() = default;
};
template <typename... Strings> struct StringRefStorage
{
static constexpr size_t stringSizeHelper() noexcept
{
// same as:
// return (0 + ... + std::extent_v<Strings>);
// but not using the fold expression to avoid exceeding compiler limits
size_t total = 0;
int sizes[] = { std::extent_v<Strings>... };
for (int n : sizes)
total += n;
return size_t(total);
}
static constexpr int StringCount = sizeof...(Strings);
static constexpr size_t StringSize = stringSizeHelper();
static_assert(StringSize <= MaxStringSize, "Meta Object data is too big");
const char *inputs[StringCount];
constexpr StringRefStorage(const Strings &... strings) noexcept
: inputs{ strings... }
{ }
constexpr void
writeTo(uint (&offsets)[2 * StringCount], char (&data)[StringSize]) const noexcept
{
int sizes[] = { std::extent_v<Strings>... };
uint offset = 0;
char *output = data;
for (size_t i = 0; i < sizeof...(Strings); ++i) {
// copy the input string, including the terminating null
int len = sizes[i];
for (int j = 0; j < len; ++j)
output[offset + j] = inputs[i][j];
offsets[2 * i] = offset + sizeof(offsets);
offsets[2 * i + 1] = len - 1;
offset += len;
}
}
constexpr auto create() const noexcept
{
StringData<2 * StringCount, StringSize> result;
writeTo(result.offsetsAndSizes, result.stringdata0);
return result;
}
};
template <uint... Nx> constexpr auto stringData(const char (&...strings)[Nx])
{
return StringRefStorage(strings...).create();
}
template <typename FuncType> inline bool indexOfMethod(void **_a, FuncType f, int index) noexcept
{
int *result = static_cast<int *>(_a[0]);
auto candidate = reinterpret_cast<FuncType *>(_a[1]);
if (*candidate != f)
return false;
*result = index;
return true;
}
template <typename Prop, typename Value> inline bool setProperty(Prop &property, Value &&value)
{
if (property == value)
return false;
property = std::forward<Value>(value);
return true;
}
struct NoType {};
namespace detail {
template<typename Enum> constexpr int payloadSizeForEnum()
{
// How many uint blocks do we need to store the values of this enum and the
// string indices for the enumeration labels? We only support 8- 16-, 32-
// and 64-bit enums at the time of this writing, so this code is extra
// pedantic allowing for 48-, 96-, 128-bit, etc.
int n = int(sizeof(Enum) + sizeof(uint)) - 1;
return 1 + n / sizeof(uint);
}
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] = {};
};
// By default, we allow metatypes for incomplete types to be stored in the
// metatype array, but we provide a way to require them to be complete by using
// void as the Unique type (used by moc if --require-complete-types is passed
// or some internal heuristic for QML matches) or by using the enum below, for
// properties and enums.
enum TypeCompletenessForMetaType : bool {
TypeMayBeIncomplete = false,
TypeMustBeComplete = true,
};
template <bool TypeMustBeComplete, typename... T> struct MetaTypeList
{
static constexpr int count() { return sizeof...(T); }
template <typename Unique, typename Result> static constexpr void
copyTo(Result &result, uint &metatypeoffset)
{
if constexpr (count()) {
using namespace QtPrivate;
using U = std::conditional_t<TypeMustBeComplete, void, Unique>;
const QMetaTypeInterface *metaTypes[] = {
qTryMetaTypeInterfaceForType<U, T>()...
};
for (const QMetaTypeInterface *mt : metaTypes)
result.relocatingData.metaTypes[metatypeoffset++] = mt;
}
}
};
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(); }
static constexpr int metaTypeCount()
{
// ditto again
int total = 0;
int sizes[] = { 0, decltype(Block::metaTypes())::count()... };
for (int n : sizes)
total += n;
return total;
}
template <typename Unique, typename Result> constexpr void
copyTo(Result &result, size_t dataoffset, uint &metatypeoffset) const
{
uint *ptr = result.staticData.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.adjustOffsets(ptr, uint(dataoffset), uint(payloadoffset), metatypeoffset);
// copy the metatypes
decltype(input.metaTypes())::template copyTo<Unique>(result, metatypeoffset);
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];
}
}
};
template <typename PropertyType> 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 auto metaTypes()
{ return detail::MetaTypeList<detail::TypeMustBeComplete, PropertyType>{}; }
static constexpr void adjustOffsets(uint *, uint, uint, uint) noexcept {}
};
template <typename Enum, int N = 0>
struct EnumData : detail::UintDataBlock<5, N * detail::payloadSizeForEnum<Enum>()>
{
private:
static_assert(sizeof(Enum) <= 2 * 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]) const
{
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);
}
if constexpr (sizeof(Enum) > sizeof(uint)) {
static_assert(N == 0, "Unimplemented: merging with non-empty EnumData");
result.header[2] |= QtMocConstants::EnumIs64Bit;
for (auto entry : entries) {
auto value = qToUnderlying(entry.value);
result.payload[o++] = uint(value >> 32);
}
}
return result;
}
static constexpr auto metaTypes()
{ return detail::MetaTypeList<detail::TypeMustBeComplete, Enum>{}; }
static constexpr void
adjustOffsets(uint *ptr, uint dataoffset, uint payloadoffset, uint metatypeoffset) noexcept
{
ptr[dataoffset + 4] += uint(payloadoffset);
(void) metatypeoffset;
}
};
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 auto metaTypes()
{
using namespace QtMocConstants;
if constexpr (std::is_same_v<Ret, NoType>) {
// constructors have no return type
static_assert((ExtraFlags & MethodConstructor) == MethodConstructor,
"NoType return type used on a non-constructor");
static_assert((ExtraFlags & MethodIsConst) == 0,
"Constructors cannot be const");
return detail::MetaTypeList<detail::TypeMayBeIncomplete, Args...>{};
} else {
static_assert((ExtraFlags & MethodConstructor) != MethodConstructor,
"Constructors must use NoType as return type");
return detail::MetaTypeList<detail::TypeMayBeIncomplete, Ret, Args...>{};
}
}
static constexpr void
adjustOffsets(uint *ptr, uint dataoffset, uint payloadoffset, uint metatypeoffset) noexcept
{
if constexpr (IsRevisioned)
++payloadoffset;
ptr[dataoffset + 2] += uint(payloadoffset);
ptr[dataoffset + 5] = metatypeoffset;
}
constexpr
FunctionData(uint nameIndex, uint tagIndex, 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] = 0; // will be set in adjustOffsets()
uint *p = this->payload;
if constexpr (ExtraFlags & QtMocConstants::MethodRevisioned)
++p;
*p++ = returnType;
if constexpr (sizeof...(Args)) {
for (uint i = 0; i < sizeof...(Args); ++i)
*p++ = params[i].typeIdx;
for (uint i = 0; i < sizeof...(Args); ++i)
*p++ = params[i].nameIdx;
} else {
Q_UNUSED(params);
}
}
constexpr
FunctionData(uint nameIndex, uint tagIndex, uint flags, uint revision,
uint returnType, ParametersArray params = {})
#ifdef __cpp_concepts
requires(IsRevisioned)
#endif
: FunctionData(nameIndex, tagIndex, 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 Base = FunctionData<F, QtMocConstants::MethodConstructor>;
// the name for a constructor is always the class name (string index zero)
// and it has no return type
constexpr ConstructorData(uint tagIndex, uint flags, typename Base::ParametersArray params = {})
: Base(0, tagIndex, flags, QMetaType::UnknownType, params)
{}
};
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 Base = FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodConstructor>;
// the name for a constructor is always the class name (string index zero)
// and it has no return type
constexpr RevisionedConstructorData(uint tagIndex, uint flags, uint revision,
typename Base::ParametersArray params = {})
: Base(0, tagIndex, flags, revision, QMetaType::UnknownType, params)
{}
};
template <typename ObjectType, typename Unique, typename Strings,
typename Methods, typename Properties, typename Enums,
typename Constructors = UintData<>, typename ClassInfo = detail::UintDataBlock<0, 0>>
constexpr auto metaObjectData(uint flags, const Strings &strings,
const Methods &methods, const Properties &properties,
const Enums &enums, const Constructors &constructors = {},
const ClassInfo &classInfo = {})
{
constexpr uint MetaTypeCount = Properties::metaTypeCount()
+ Enums::metaTypeCount()
+ 1 // the gadget's or void
+ Methods::metaTypeCount()
+ Constructors::metaTypeCount();
constexpr uint HeaderSize = 14;
constexpr uint TotalSize = HeaderSize
+ Properties::dataSize()
+ Enums::dataSize()
+ Methods::dataSize()
+ Constructors::dataSize()
+ ClassInfo::headerSize() // + ClassInfo::payloadSize()
+ 1; // empty EOD
MetaObjectContents<TotalSize, 2 * Strings::StringCount, Strings::StringSize,
MetaTypeCount> result = {};
strings.writeTo(result.staticData.stringdata, result.staticData.strings);
uint dataoffset = HeaderSize;
uint metatypeoffset = 0;
uint *data = result.staticData.data;
data[0] = QtMocConstants::OutputRevision;
data[1] = 0; // class name index (it's always 0)
data[2] = ClassInfo::headerSize() / 2;
data[3] = ClassInfo::headerSize() ? dataoffset : 0;
q20::copy_n(classInfo.header, classInfo.headerSize(), data + dataoffset);
dataoffset += ClassInfo::headerSize();
data[6] = properties.count();
data[7] = properties.count() ? dataoffset : 0;
properties.template copyTo<Unique>(result, dataoffset, metatypeoffset);
dataoffset += properties.dataSize();
data[8] = enums.count();
data[9] = enums.count() ? dataoffset : 0;
enums.template copyTo<Unique>(result, dataoffset, metatypeoffset);
dataoffset += enums.dataSize();
// the meta type referring to the object itself
result.relocatingData.metaTypes[metatypeoffset++] = QMetaType::fromType<ObjectType>().iface();
data[4] = methods.count();
data[5] = methods.count() ? dataoffset : 0;
methods.template copyTo<Unique>(result, dataoffset, metatypeoffset);
dataoffset += methods.dataSize();
data[10] = constructors.count();
data[11] = constructors.count() ? dataoffset : 0;
constructors.template copyTo<Unique>(result, dataoffset, metatypeoffset);
dataoffset += constructors.dataSize();
data[12] = flags;
// count the number of signals
if constexpr (Methods::count()) {
constexpr uint MethodHeaderSize = Methods::headerSize() / Methods::count();
const uint *ptr = &data[data[5]];
const uint *end = &data[data[5] + MethodHeaderSize * Methods::count()];
for ( ; ptr < end; ptr += MethodHeaderSize) {
if ((ptr[4] & QtMocConstants::MethodSignal) == 0)
break;
++data[13];
}
}
return result;
}
template <typename T> inline std::enable_if_t<std::is_enum_v<T>> assignFlags(void *v, T t) noexcept
{
*static_cast<T *>(v) = t;
}
template <typename T> inline std::enable_if_t<QtPrivate::IsQFlags<T>::value> assignFlags(void *v, T t) noexcept
{
*static_cast<T *>(v) = t;
}
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
template <typename T>
Q_DECL_DEPRECATED_X("Returning int/uint from a Q_PROPERTY that is a Q_FLAG is deprecated; "
"please update to return the actual property's type")
inline void assignFlagsFromInteger(QFlags<T> &f, int i) noexcept
{
f = QFlag(i);
}
template <typename T, typename I>
inline std::enable_if_t<QtPrivate::IsQFlags<T>::value && sizeof(T) == sizeof(int) && std::is_integral_v<I>>
assignFlags(void *v, I i) noexcept
{
assignFlagsFromInteger(*static_cast<T *>(v), i);
}
#endif // Qt 7
} // namespace QtMocHelpers
QT_END_NAMESPACE
QT_USE_NAMESPACE
#endif // QTMOCHELPERS_H