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<long sequence of numbers>" 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 <ulf.hermann@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
bb10
Thiago Macieira 2022-06-18 14:47:28 -07:00
parent c23b595769
commit 7ceba9c472
2 changed files with 79 additions and 0 deletions

View File

@ -23,6 +23,59 @@
QT_BEGIN_NAMESPACE
namespace QtMocHelpers {
template <uint... Nx> 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<uint>::max)()),
size_t((std::numeric_limits<qsizetype>::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 <uint... Nx> constexpr auto stringData(const char (&...strings)[Nx])
{
StringData<Nx...> 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

View File

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