moc: add constexpr functions to generate the uint data

These implement the current (revision 13) meta object uint data. The
location of the data blocks is slightly different from what moc
produces, which in turn is different from QMetaObjectBuilder, but it's
compatible with any reader that doesn't make undue assumptions about
just how data is laid out.

In fact, revision 13 was created specifically so these functiosn could
exist, as placing the method revisions where they were in revision 12
was too cumbersome.

Change-Id: I8a96935cf6c742259c9dfffd17ea6930c947b768
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
This commit is contained in:
Thiago Macieira 2024-08-10 08:57:45 -07:00
parent dfe71d1d23
commit 3e6eb30a80
2 changed files with 962 additions and 2 deletions

View File

@ -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

View File

@ -4,18 +4,40 @@
// Testing qtmochelpers.h is probably pointless... if there's a problem with it
// then you most likely can't compile this test in the first place.
#include <QtCore/qtmochelpers.h>
#include "qtmocconstants.h"
#include <QTest>
#include <QtCore/qobject.h>
#include <initializer_list>
#include <q20algorithm.h>
QT_BEGIN_NAMESPACE
namespace QtMocHelpers {
} QT_END_NAMESPACE
class tst_MocHelpers : public QObject
{
Q_OBJECT
private slots:
void stringData();
// parts of the uint array
void classinfoData();
void classinfoDataGroup();
void enumUintData();
void enumUintGroup();
void propertyUintData();
void propertyUintGroup();
void methodUintData();
void methodUintGroup();
void constructorUintData();
void constructorUintGroup();
void emptyUintArray();
void uintArrayNoMethods();
void uintArray();
};
template <int Count, size_t StringSize>
@ -46,5 +68,593 @@ void tst_MocHelpers::stringData()
#undef CHECK
}
void tst_MocHelpers::classinfoData()
{
{
auto result = QtMocHelpers::ClassInfos({{1, 2}});
QCOMPARE(result.headerSize(), 2U);
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 2U);
}
{
auto result = QtMocHelpers::ClassInfos({{1, 2}, {3, 4}});
QCOMPARE(result.headerSize(), 4U);
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 2U);
QCOMPARE(result.header[2], 3U);
QCOMPARE(result.header[3], 4U);
}
}
template <size_t N> static void checkClassInfos(const std::array<uint, N> &data)
{
QCOMPARE(data[2], 2U);
QCOMPARE_GE(data[3], 14U);
const uint *classinfos = data.data() + data[3];
QCOMPARE(classinfos[0], 1U);
QCOMPARE(classinfos[1], 2U);
QCOMPARE(classinfos[2], 3U);
QCOMPARE(classinfos[3], 4U);
}
void tst_MocHelpers::classinfoDataGroup()
{
constexpr auto data = QtMocHelpers::metaObjectData(0,
QtMocHelpers::UintData{}, QtMocHelpers::UintData{},
QtMocHelpers::UintData{}, QtMocHelpers::UintData{},
QtMocHelpers::ClassInfos({{1, 2}, {3, 4}}));
checkClassInfos(data.data);
}
template <typename E, int N> void enumUintData_check(const E (&values)[N])
{
using namespace QtMocHelpers;
// make an array of dummy offsets
typename EnumData<E>::EnumEntry namesAndOffsets[N];
for (int i = 0; i < N; ++i) {
namesAndOffsets[i].nameIndex = 2 * (i + 7);
namesAndOffsets[i].value = values[i];
}
auto result = EnumData<E>(0, 0, 0).add(namesAndOffsets);
for (uint i = 0; i < N; ++i) {
QCOMPARE(result.payload[2 * i + 0], uint(namesAndOffsets[i].nameIndex));
QCOMPARE(result.payload[2 * i + 1], uint(values[i]));
}
}
enum E1 { AnEnumValue };
enum class E2 { V0 = INT_MAX, V1 = INT_MIN };
enum class E3 : int { V = 0x1111'2222, V2 = -V };
void tst_MocHelpers::enumUintData()
{
using namespace QtMocHelpers;
using namespace QtMocConstants;
{
auto result = QtMocHelpers::EnumData<E1>(1, 1, EnumFlags{});
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 1U);
QCOMPARE(result.header[2], EnumFlags{});
QCOMPARE(result.header[3], 0U);
QCOMPARE(result.payloadSize(), 0U);
}
{
auto result = QtMocHelpers::EnumData<QFlags<E1>>(1, 2, EnumIsFlag);
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 2U);
QCOMPARE(result.header[2], EnumIsFlag);
QCOMPARE(result.header[3], 0U);
QCOMPARE(result.payloadSize(), 0U);
}
{
auto result = QtMocHelpers::EnumData<E1>(1, 1, EnumFlags{}).add({ { 1, E1::AnEnumValue } });
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 1U);
QCOMPARE(result.header[2], EnumFlags{});
QCOMPARE(result.header[3], 1U);
QCOMPARE(result.payload[0], 1U);
QCOMPARE(result.payload[1], uint(E1::AnEnumValue));
}
{
auto result = QtMocHelpers::EnumData<QFlags<E1>>(1, 2, EnumFlags{}).add({ { 1, E1::AnEnumValue } });
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 2U);
QCOMPARE(result.header[2], uint(EnumIsFlag));
QCOMPARE(result.header[3], 1U);
QCOMPARE(result.payload[0], 1U);
QCOMPARE(result.payload[1], uint(E1::AnEnumValue));
}
{
constexpr auto result = QtMocHelpers::EnumData<E3>(1, 1, EnumIsScoped)
.add({ { 2, E3::V }, {3, E3::V2 }, });
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 1U);
QCOMPARE(result.header[2], EnumIsScoped);
QCOMPARE(result.header[3], 2U);
QCOMPARE(result.payload[0], 2U);
QCOMPARE(result.payload[1], uint(E3::V));
QCOMPARE(result.payload[2], 3U);
QCOMPARE(result.payload[3], uint(E3::V2));
}
QTest::setThrowOnFail(true);
{
enum E { E0, E1 = -1, E2 = 123, E3 = INT_MIN };
enumUintData_check({E0, E1, E2, E3});
}
}
template <typename Data> void testUintData(const Data &data)
{
uint count = 0;
size_t headerSize = 0;
size_t payloadSize = 0;
data.forEach([&](const auto &block) {
++count;
headerSize += block.headerSize();
payloadSize += block.payloadSize();
});
QCOMPARE(data.count(), count);
QCOMPARE(data.headerSize(), headerSize);
QCOMPARE(data.payloadSize(), payloadSize);
}
template <size_t N> static void checkEnums(const std::array<uint, N> &data)
{
using namespace QtMocConstants;
QCOMPARE(data[8], 4U);
QCOMPARE_NE(data[9], 0U);
const uint *header = data.data() + data[9];
// E1:
QCOMPARE(header[0 + 0], 1U);
QCOMPARE(header[0 + 1], 1U);
QCOMPARE(header[0 + 2], 0U);
QCOMPARE(header[0 + 3], 1U);
QCOMPARE_GE(header[0 + 4], 14U);
const uint *payload = data.data() + header[0 + 4];
QCOMPARE(payload[0], 3U);
QCOMPARE(payload[1], uint(E1::AnEnumValue));
// E3:
QCOMPARE(header[5 + 0], 4U);
QCOMPARE(header[5 + 1], 5U);
QCOMPARE(header[5 + 2], EnumIsFlag | EnumIsScoped);
QCOMPARE(header[5 + 3], 2U);
QCOMPARE_GE(header[5 + 4], 14U);
payload = data.data() + header[5 + 4];
QCOMPARE(payload[0], 6U);
QCOMPARE(payload[1], uint(E3::V));
QCOMPARE(payload[2], 8U);
QCOMPARE(payload[3], uint(E3::V2));
// E2:
QCOMPARE(header[10 + 0], 7U);
QCOMPARE(header[10 + 1], 6U);
QCOMPARE(header[10 + 2], EnumIsFlag | EnumIsScoped);
QCOMPARE(header[10 + 3], 2U);
QCOMPARE_GE(header[10 + 4], 14U);
payload = data.data() + header[10 + 4];
QCOMPARE(payload[0], 7U);
QCOMPARE(payload[1], uint(E2::V0));
QCOMPARE(payload[2], 10U);
QCOMPARE(payload[3], uint(E2::V1));
// QFlags<E1>
QCOMPARE(header[15 + 0], 11U);
QCOMPARE(header[15 + 1], 1U);
QCOMPARE(header[15 + 2], EnumIsFlag);
QCOMPARE(header[15 + 3], 1U);
QCOMPARE_GE(header[15 + 4], 14U);
payload = data.data() + header[15 + 4];
QCOMPARE(payload[0], 3U);
QCOMPARE(payload[1], uint(E1::AnEnumValue));
}
void tst_MocHelpers::enumUintGroup()
{
using namespace QtMocConstants;
QTest::setThrowOnFail(true);
constexpr QtMocHelpers::UintData enums = {
QtMocHelpers::EnumData<E1>(1, 1, 0x00).add({ { 3, E1::AnEnumValue } }),
QtMocHelpers::EnumData<E3>(4, 5, EnumIsFlag | EnumIsScoped)
.add({ { 6, E3::V }, { 8, E3::V2 }, }),
QtMocHelpers::EnumData<E2>(7, 6, EnumIsFlag | EnumIsScoped)
.add({ { 7, E2::V0 }, { 10, E2::V1 }, }),
QtMocHelpers::EnumData<QFlags<E1>>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }),
};
testUintData(enums);
constexpr auto data = QtMocHelpers::metaObjectData(0,
QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, enums);
checkEnums(data.data);
}
void tst_MocHelpers::propertyUintData()
{
using namespace QtMocHelpers;
{
auto result = PropertyData(3, QMetaType::Int, 0x3, 13, 0x101);
QCOMPARE(result.payloadSize(), 0U);
QCOMPARE(result.header[0], 3U);
QCOMPARE(result.header[1], uint(QMetaType::Int));
QCOMPARE(result.header[2], 0x3U);
QCOMPARE(result.header[3], 13U);
QCOMPARE(result.header[4], 0x0101U);
}
{
// check that QMetaType doesn't override if it's an alias
auto result = PropertyData(3, 0x80000000 | 4, 0x03);
QCOMPARE(result.header[1], 0x80000000U | 4);
}
{
// Or derived from
struct Dummy : QString {};
auto result = PropertyData(3, 0x80000000 | 4, 0x03);
QCOMPARE(result.header[1], 0x80000000U | 4);
}
}
template <size_t N> static void checkProperties(const std::array<uint, N> &data)
{
QCOMPARE(data[6], 3U);
QCOMPARE_NE(data[7], 0U);
const uint *header = data.data() + data[7];
QCOMPARE(header[0 + 0], 3U);
QCOMPARE(header[0 + 1], uint(QMetaType::Int));
QCOMPARE(header[0 + 2], 0x3U);
QCOMPARE(header[0 + 3], 13U);
QCOMPARE(header[0 + 4], 0x0101U);
QCOMPARE(header[5 + 0], 4U);
QCOMPARE(header[5 + 1], 0x80000000U | 5);
QCOMPARE(header[5 + 2], 0x3U);
QCOMPARE(header[5 + 3], uint(-1));
QCOMPARE(header[5 + 4], 0U);
QCOMPARE(header[10 + 0], 6U);
QCOMPARE(header[10 + 1], 0x80000000U | 7);
QCOMPARE(header[10 + 2], 0x3U);
QCOMPARE(header[10 + 3], uint(-1));
QCOMPARE(header[10 + 4], 0U);
}
void tst_MocHelpers::propertyUintGroup()
{
QTest::setThrowOnFail(true);
constexpr QtMocHelpers::UintData properties = {
QtMocHelpers::PropertyData(3, QMetaType::Int, 0x3, 13, 0x101),
QtMocHelpers::PropertyData(4, 0x80000000 | 5, 0x03),
QtMocHelpers::PropertyData(6, 0x80000000 | 7, 0x03)
};
testUintData(properties);
constexpr auto data = QtMocHelpers::metaObjectData(0, QtMocHelpers::UintData{}, properties, QtMocHelpers::UintData{});
checkProperties(data.data);
}
void tst_MocHelpers::methodUintData()
{
using namespace QtMocHelpers;
using namespace QtMocConstants;
{
auto result = SignalData<void()>(1, 2, 0, AccessPublic, QMetaType::Void, {});
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 0U);
QCOMPARE(result.header[3], 2U);
QCOMPARE(result.header[4], AccessPublic | MethodSignal);
QCOMPARE(result.header[5], 0U);
QCOMPARE(result.payload[0], uint(QMetaType::Void));
}
{
auto result = SlotData<void (const QString &) const>(1, 2, 0, AccessPublic,
QMetaType::Void, { { { QMetaType::QString, 1000 } } });
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 1U);
QCOMPARE(result.header[3], 2U);
QCOMPARE(result.header[4], AccessPublic | MethodSlot | MethodIsConst);
QCOMPARE(result.header[5], 0U);
QCOMPARE(result.payload[0], uint(QMetaType::Void));
QCOMPARE(result.payload[1], uint(QMetaType::QString));
QCOMPARE(result.payload[2], 1000U);
}
{
auto result = RevisionedSlotData<void (const QString &)>(1, 2, 0, AccessPublic, 0xff01,
QMetaType::Void, { { { QMetaType::QString, 1000 } } });
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 1U);
QCOMPARE(result.header[3], 2U);
QCOMPARE(result.header[4], AccessPublic | MethodSlot | MethodRevisioned);
QCOMPARE(result.header[5], 0U);
QCOMPARE(result.payload[0], 0xff01U);
QCOMPARE(result.payload[1], uint(QMetaType::Void));
QCOMPARE(result.payload[2], uint(QMetaType::QString));
QCOMPARE(result.payload[3], 1000U);
}
}
template <size_t N> static void checkMethods(const std::array<uint, N> &data)
{
using namespace QtMocConstants;
QCOMPARE(data[4], 3U);
QCOMPARE_NE(data[5], 0U);
const uint *header = data.data() + data[5];
// signals: void signal()
QCOMPARE(header[0], 1U);
QCOMPARE(header[1], 0U);
QCOMPARE_NE(header[2], 0U);
QCOMPARE(header[3], 2U);
QCOMPARE(header[4], AccessPublic | MethodSignal | MethodRevisioned);
QCOMPARE(header[5], 6U); // ###
const uint *payload = data.data() + header[2];
QCOMPARE(payload[-1], 0x0509U);
QCOMPARE(payload[0], uint(QMetaType::Void));
// signals: void signal(E1, Dummy) [Dummy = QString]
header += 6;
QCOMPARE(header[0], 3U);
QCOMPARE(header[1], 2U);
QCOMPARE_NE(header[2], 0U);
QCOMPARE(header[3], 2U);
QCOMPARE(header[4], AccessPublic | MethodSignal);
QCOMPARE(header[5], 7U); // ###
payload = data.data() + header[2];
QCOMPARE(payload[0], uint(QMetaType::Void));
QCOMPARE(payload[1], 0x80000000U | 4); // not a builtin type
QCOMPARE(payload[2], 0x80000000U | 5);
QCOMPARE(payload[3], 6U);
QCOMPARE(payload[4], 7U);
// public slots: bool slot(QString &) const
header += 6;
QCOMPARE(header[0], 8U);
QCOMPARE(header[1], 1U);
QCOMPARE_NE(header[2], 0U);
QCOMPARE(header[3], 2U);
QCOMPARE(header[4], AccessPublic | MethodSlot | MethodIsConst);
QCOMPARE(header[5], 9U); // ###
payload = data.data() + header[2];
QCOMPARE(payload[0], uint(QMetaType::Bool));
QCOMPARE(payload[1], 0x80000000U | 10); // not a builtin type
QCOMPARE(payload[2], 11U);
}
void tst_MocHelpers::methodUintGroup()
{
QTest::setThrowOnFail(true);
using Dummy = QString;
constexpr QtMocHelpers::UintData methods = {
QtMocHelpers::RevisionedSignalData<void()>(1, 2, 6, QtMocConstants::AccessPublic, 0x509,
QMetaType::Void, {{ }}
),
QtMocHelpers::SignalData<void (E1, Dummy)>(3, 2, 7, QtMocConstants::AccessPublic,
QMetaType::Void, {{ { 0x80000000 | 4, 6 }, { 0x80000000 | 5, 7 }} }
),
QtMocHelpers::SlotData<bool (QString &) const>(8, 2, 9, QtMocConstants::AccessPublic,
QMetaType::Bool, {{ { 0x80000000 | 10, 11 } }}
)
};
testUintData(methods);
constexpr auto data = QtMocHelpers::metaObjectData(0, methods, QtMocHelpers::UintData{},
QtMocHelpers::UintData{});
checkMethods(data.data);
}
void tst_MocHelpers::constructorUintData()
{
constexpr uint NoType = 0x80000000 | 1;
using namespace QtMocHelpers;
using namespace QtMocConstants;
{
auto result = ConstructorData<QtMocHelpers::NoType()>(1, 2, 0, AccessPublic, NoType, {});
QCOMPARE(result.header[0], 1U);
QCOMPARE(result.header[1], 0U);
QCOMPARE(result.header[3], 2U);
QCOMPARE(result.header[4], AccessPublic | MethodConstructor);
QCOMPARE(result.header[5], 0U);
QCOMPARE(result.payload[0], NoType);
}
{
auto result = ConstructorData<QtMocHelpers::NoType(QObject *)>(0, 1, 0, AccessPublic, NoType,
{{ { QMetaType::QObjectStar, 2 } }});
QCOMPARE(result.header[0], 0U);
QCOMPARE(result.header[1], 1U);
QCOMPARE(result.header[3], 1U);
QCOMPARE(result.header[4], AccessPublic | MethodConstructor);
QCOMPARE(result.header[5], 1U);
QCOMPARE(result.payload[0], NoType);
QCOMPARE(result.payload[1], uint(QMetaType::QObjectStar));
QCOMPARE(result.payload[2], 2U);
}
}
template <size_t N> static void checkConstructors(const std::array<uint, N> &data)
{
using namespace QtMocConstants;
QCOMPARE(data[10], 3U);
QCOMPARE_NE(data[11], 0U);
const uint *header = data.data() + data[11];
// Constructor(QObject *)
QCOMPARE(header[0], 0U);
QCOMPARE(header[1], 1U);
QCOMPARE_NE(header[2], 0U);
QCOMPARE(header[3], 1U);
QCOMPARE(header[4], AccessPublic | MethodConstructor);
QCOMPARE_GT(header[5], 0U);
const uint *payload = data.data() + header[2];
QCOMPARE(payload[0], 0x80000000U | 1);
QCOMPARE(payload[1], uint(QMetaType::QObjectStar));
// Constructor() [cloned from the previous with a default argument]
header += 6;
QCOMPARE(header[0], 0U);
QCOMPARE(header[1], 0U);
QCOMPARE_NE(header[2], 0U);
QCOMPARE(header[3], 1U);
QCOMPARE(header[4], AccessPublic | MethodConstructor | MethodCloned);
QCOMPARE_GT(header[5], 0U);
payload = data.data() + header[2];
QCOMPARE(payload[0], 0x80000000U | 1);
// Constructor(const QString &)
header += 6;
QCOMPARE(header[0], 0U);
QCOMPARE(header[1], 1U);
QCOMPARE_NE(header[2], 0U);
QCOMPARE(header[3], 1U);
QCOMPARE(header[4], AccessPublic | MethodConstructor);
QCOMPARE_GT(header[5], 0U);
payload = data.data() + header[2];
QCOMPARE(payload[0], 0x80000000U | 1);
QCOMPARE(payload[1], uint(QMetaType::QString));
}
void tst_MocHelpers::constructorUintGroup()
{
using QtMocHelpers::NoType;
QTest::setThrowOnFail(true);
constexpr QtMocHelpers::UintData constructors = {
QtMocHelpers::ConstructorData<NoType(QObject *)>(0, 1, 1, QtMocConstants::AccessPublic,
0x80000000 | 1, {{ { QMetaType::QObjectStar, 2 } }}
),
QtMocHelpers::ConstructorData<NoType()>(0, 1, 2, QtMocConstants::AccessPublic | QtMocConstants::MethodCloned,
0x80000000 | 1, {{ }}
),
QtMocHelpers::ConstructorData<NoType(const QString &)>(0, 1, 2, QtMocConstants::AccessPublic,
0x80000000 | 1, {{ { QMetaType::QString, 3 }, }}
)
};
testUintData(constructors);
constexpr auto data = QtMocHelpers::metaObjectData(0,
QtMocHelpers::UintData{}, QtMocHelpers::UintData{},
QtMocHelpers::UintData{}, constructors);
checkConstructors(data.data);
}
template <size_t N> static void checkUintArrayGeneric(const std::array<uint, N> &data, uint flags = 0)
{
using namespace QtMocConstants;
QCOMPARE(data[0], uint(OutputRevision));
QCOMPARE(data[1], 0U);
QCOMPARE(data[12], flags);
QCOMPARE(data[N-1], 0U);
// check the offsets are valid
QCOMPARE_LT(data[2], N); // classinfos
QCOMPARE_LT(data[4], N); // methods
QCOMPARE_LT(data[6], N); // properties
QCOMPARE_LT(data[8], N); // enums
QCOMPARE_LT(data[10], N); // constructors
}
void tst_MocHelpers::emptyUintArray()
{
using namespace QtMocConstants;
constexpr auto data = QtMocHelpers::metaObjectData(MetaObjectFlag{},
QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, QtMocHelpers::UintData{});
QTest::setThrowOnFail(true);
checkUintArrayGeneric(data.data, MetaObjectFlag{});
QTest::setThrowOnFail(false);
// check it says nothing was added
QCOMPARE(data.data[2], 0U); // classinfos
QCOMPARE(data.data[4], 0U); // methods
QCOMPARE(data.data[6], 0U); // properties
QCOMPARE(data.data[8], 0U); // enums
QCOMPARE(data.data[10], 0U); // constructors
QCOMPARE(data.data[13], 0U); // signals
}
void tst_MocHelpers::uintArrayNoMethods()
{
using namespace QtMocConstants;
constexpr auto data = QtMocHelpers::metaObjectData(PropertyAccessInStaticMetaCall,
QtMocHelpers::UintData{},
QtMocHelpers::UintData{
QtMocHelpers::PropertyData(3, QMetaType::Int, 0x3, 13, 0x101),
QtMocHelpers::PropertyData(4, 0x80000000 | 5, 0x03),
QtMocHelpers::PropertyData(6, 0x80000000 | 7, 0x03),
}, QtMocHelpers::UintData{
QtMocHelpers::EnumData<E1>(1, 1, 0x00).add({ { 3, E1::AnEnumValue } }),
QtMocHelpers::EnumData<E3>(4, 5, EnumIsFlag | EnumIsScoped)
.add({ { 6, E3::V }, { 8, E3::V2 }, }),
QtMocHelpers::EnumData<E2>(7, 6, EnumIsFlag | EnumIsScoped)
.add({ { 7, E2::V0 }, { 10, E2::V1 }, }),
QtMocHelpers::EnumData<QFlags<E1>>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }),
}, QtMocHelpers::UintData{}, QtMocHelpers::ClassInfos({{1, 2}, {3, 4}}));
QTest::setThrowOnFail(true);
checkUintArrayGeneric(data.data, PropertyAccessInStaticMetaCall);
checkClassInfos(data.data);
checkProperties(data.data);
checkEnums(data.data);
QTest::setThrowOnFail(false);
QCOMPARE(data.data[4], 0U); // methods
QCOMPARE(data.data[10], 0U); // constructors
QCOMPARE(data.data[13], 0U); // signals
}
void tst_MocHelpers::uintArray()
{
using Dummy = QString;
using QtMocHelpers::NoType;
using namespace QtMocConstants;
constexpr auto data = QtMocHelpers::metaObjectData(PropertyAccessInStaticMetaCall,
QtMocHelpers::UintData{
QtMocHelpers::RevisionedSignalData<void()>(1, 2, 6, QtMocConstants::AccessPublic, 0x509,
QMetaType::Void, {{ }}
),
QtMocHelpers::SignalData<void (E1, Dummy)>(3, 2, 7, QtMocConstants::AccessPublic,
QMetaType::Void, {{ { 0x80000000 | 4, 6 }, { 0x80000000 | 5, 7 }} }
),
QtMocHelpers::SlotData<bool (QString &) const>(8, 2, 9, QtMocConstants::AccessPublic,
QMetaType::Bool, {{ { 0x80000000 | 10, 11 } }}
)
},
QtMocHelpers::UintData{
QtMocHelpers::PropertyData(3, QMetaType::Int, 0x3, 13, 0x101),
QtMocHelpers::PropertyData(4, 0x80000000 | 5, 0x03),
QtMocHelpers::PropertyData(6, 0x80000000 | 7, 0x03),
}, QtMocHelpers::UintData{
QtMocHelpers::EnumData<E1>(1, 1, 0x00).add({ { 3, E1::AnEnumValue } }),
QtMocHelpers::EnumData<E3>(4, 5, EnumIsFlag | EnumIsScoped)
.add({ { 6, E3::V }, { 8, E3::V2 }, }),
QtMocHelpers::EnumData<E2>(7, 6, EnumIsFlag | EnumIsScoped)
.add({ { 7, E2::V0 }, { 10, E2::V1 }, }),
QtMocHelpers::EnumData<QFlags<E1>>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }),
},
QtMocHelpers::UintData{
QtMocHelpers::ConstructorData<NoType(QObject *)>(0, 1, 1, QtMocConstants::AccessPublic,
0x80000000 | 1, {{ { QMetaType::QObjectStar, 2 } }}
),
QtMocHelpers::ConstructorData<NoType()>(0, 1, 2, QtMocConstants::AccessPublic | QtMocConstants::MethodCloned,
0x80000000 | 1, {{ }}
),
QtMocHelpers::ConstructorData<NoType(const QString &)>(0, 1, 3, QtMocConstants::AccessPublic,
0x80000000 | 1, {{ { QMetaType::QString, 3 }, }}
)
}, QtMocHelpers::ClassInfos({{1, 2}, {3, 4}}));
QTest::setThrowOnFail(true);
checkUintArrayGeneric(data.data, PropertyAccessInStaticMetaCall);
checkClassInfos(data.data);
checkProperties(data.data);
checkEnums(data.data);
checkMethods(data.data);
checkConstructors(data.data);
}
QTEST_MAIN(tst_MocHelpers)
#include "tst_mochelpers.moc"