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"); //