From 7ceba9c4723846bc77d94c7bfc62c1a8ff1d9138 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 18 Jun 2022 14:47:28 -0700 Subject: [PATCH] moc: use C++ constexpr to generate the meta object string data This avoids having to rely on moc precalculating everything and getting its sums right, as the main integer table only has a string index (unlike my original 5.0 plan which would have stored the offset). For example, for: struct Object : QObject { Q_OBJECT signals: void changed(const QVariant &v); } moc now generates: struct qt_meta_stringdata_Object_t {}; static constexpr auto qt_meta_stringdata_Object = QtMocHelpers::stringData( "Object", "foobar", "", "v" ); Notes on the implementation: * the old code is retained because we need moc 6.5 to generate code compatible with QtCore 6.2-6.4. It's marked for removal in Qt 6.9. * the struct qt_meta_stringdata_%s_t remains because we need a TU-specific type for QtPrivate::is_complete (via qt_incomplete_metaTypeArray). This can be cleaned up. * after this change, GDB complains while loading QtCore: warning: internal error: string "StringData" failed to be canonicalized but debugging does not appear to be impacted. It's just annoying because it shows up when QtTest detects a crash too. Change-Id: Id0fb9ab0089845ee8843fffd16f9d5493e9bd708 Reviewed-by: Ulf Hermann Reviewed-by: Fabian Kosmale --- src/corelib/kernel/qtmochelpers.h | 53 +++++++++++++++++++++++++++++++ src/tools/moc/generator.cpp | 26 +++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/corelib/kernel/qtmochelpers.h b/src/corelib/kernel/qtmochelpers.h index 52880f86c1..7bdaaf276b 100644 --- a/src/corelib/kernel/qtmochelpers.h +++ b/src/corelib/kernel/qtmochelpers.h @@ -23,6 +23,59 @@ QT_BEGIN_NAMESPACE namespace QtMocHelpers { +template struct StringData +{ + static constexpr size_t calculateStringSize() + { + // same as: + // return (0 + ... + Nx); + // but not using the fold expression to avoid exceeding compiler limits + size_t total = 0; + uint sizes[] = { Nx... }; + for (uint n : sizes) + total += n; + return total; + } + + // 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::max)()), + size_t((std::numeric_limits::max)())); + + static constexpr size_t StringSize = calculateStringSize(); + static_assert(StringSize <= MaxStringSize, "Meta Object data is too big"); + + uint offsetsAndSizes[2 * sizeof...(Nx)] = {}; + char stringdata0[StringSize] = {}; + constexpr StringData() = default; +}; + +template constexpr auto stringData(const char (&...strings)[Nx]) +{ + StringData result; + const char *inputs[] = { strings... }; + uint sizes[] = { Nx... }; + + uint offset = 0; + char *output = result.stringdata0; + for (size_t i = 0; i < sizeof...(Nx); ++i) { + // copy the input string, including the terminating null + uint len = sizes[i]; + for (uint j = 0; j < len; ++j) + output[offset + j] = inputs[i][j]; + result.offsetsAndSizes[2 * i] = offset + sizeof(result.offsetsAndSizes); + result.offsetsAndSizes[2 * i + 1] = len - 1; + offset += len; + } + + return result; +} + +#if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU_ONLY >= 1000 +// It looks like there's a bug in GCC 9 +# define QT_MOC_HAS_STRINGDATA 1 +#endif } // namespace QtMocHelpers QT_END_NAMESPACE diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 0e8ba0bb72..e9e5f167b7 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -256,9 +256,33 @@ void Generator::generateCode() // ensure the qt_meta_stringdata_XXXX_t type is local fprintf(out, "namespace {\n"); +// +// Build the strings using QtMocHelpers::StringData +// + + fprintf(out, "\n#ifdef QT_MOC_HAS_STRINGDATA\n" + "struct qt_meta_stringdata_%s_t {};\n" + "static constexpr auto qt_meta_stringdata_%s = QtMocHelpers::stringData(", + qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); + { + char comma = 0; + for (const QByteArray &str : strings) { + if (comma) + fputc(comma, out); + printStringWithIndentation(out, str); + comma = ','; + } + } + fprintf(out, "\n);\n" + "#else // !QT_MOC_HAS_STRING_DATA\n"); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0) + fprintf(out, "#error \"qtmochelpers.h not found or too old.\"\n"); +#else // // Build stringdata struct // + fprintf(out, "struct qt_meta_stringdata_%s_t {\n", qualifiedClassNameIdentifier.constData()); fprintf(out, " uint offsetsAndSizes[%d];\n", int(strings.size() * 2)); for (int i = 0; i < strings.size(); ++i) { @@ -302,7 +326,9 @@ void Generator::generateCode() // Terminate stringdata struct fprintf(out, "\n};\n"); fprintf(out, "#undef QT_MOC_LITERAL\n"); +#endif // Qt 6.9 + fprintf(out, "#endif // !QT_MOC_HAS_STRING_DATA\n"); fprintf(out, "} // unnamed namespace\n\n"); //