From 874be50e7b773698abf0b099b0b32a9cbb3cff2d Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 24 Feb 2025 16:09:18 +0100 Subject: [PATCH] QStringView: introduce a user-defined literal operator Although char16_t string literals implicitly convert to QStringView, there are corner-cases where one may want to explicitly create a QStringView out of them. A couple of examples I've found is where these string literals decay into pointers: // range is a std::initializer_list for (QStringView v : { u"foo", u"bar" }) { ... } // ternary will decay arguments void print(QStringView); print(check ? u"hi" : u"there"); When this happens the resulting code gets pessimized and polluted by runtime calls to qustrlen in order to build the QStringView objects [1]. We can restore optimal codegen by directly dealing with QStringView objects instead. Adding explicit conversions may make the code cumbersome to read, so I'm introducing a UDL for QStringView, matching the one for QString (and std::string_view). [1] for instance: https://gcc.godbolt.org/z/eY7xvEje3 Apply the new operator to a couple of places. [ChangeLog][QtCore][QStringView] Is it now possible to create QStringView objects by using the u""_sv user-defined literal. Fixes: QTBUG-123851 Change-Id: I8af7d2e211b356d284de160a222eab9e91d09500 Reviewed-by: Thiago Macieira --- src/corelib/ipc/qtipccommon.cpp | 2 +- src/corelib/text/qstringview.cpp | 40 +++++++++++++++++++ src/corelib/text/qstringview.h | 11 +++++ src/widgets/styles/qcommonstyle.cpp | 2 +- .../text/qstringview/tst_qstringview.cpp | 23 +++++++++++ 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/corelib/ipc/qtipccommon.cpp b/src/corelib/ipc/qtipccommon.cpp index 3d9a7281a00..355f6fbc602 100644 --- a/src/corelib/ipc/qtipccommon.cpp +++ b/src/corelib/ipc/qtipccommon.cpp @@ -200,7 +200,7 @@ QNativeIpcKey QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcT QStringView prefix; QStringView payload = key; // see https://learn.microsoft.com/en-us/windows/win32/termserv/kernel-object-namespaces - for (QStringView candidate : { u"Local\\", u"Global\\" }) { + for (QStringView candidate : { u"Local\\"_sv, u"Global\\"_sv }) { if (!key.startsWith(candidate)) continue; prefix = candidate; diff --git a/src/corelib/text/qstringview.cpp b/src/corelib/text/qstringview.cpp index 64d4ffc1f1d..923eed5539c 100644 --- a/src/corelib/text/qstringview.cpp +++ b/src/corelib/text/qstringview.cpp @@ -1496,4 +1496,44 @@ or the character \a ch Returns maxSize(). */ +/*! + \fn Qt::Literals::StringLiterals::operator""_sv(const char16_t *str, size_t size) + + \relates QStringView + \since 6.10 + + Literal operator that creates a QStringView out of the first + \a size characters in the char16_t string literal \a str. + + There is rarely need to explicitly construct a QStringView from a + char16_t string literal, as QStringView is implicitly constructible + from one: + + \code + QStringView greeting = u"hello"; // OK even without _sv + + void print(QStringView s); + print(u"world"); // OK even without _sv + \endcode + + To use this operator, you need to be using the corresponding + namespace(s): + + \code + using namespace Qt::Literals::StringLiterals; + auto sv = u"peace"_sv; + \endcode + + Note that the returned QStringView will span over any NUL embedded + in the string literal. This is different from passing the string + literal to QStringView's constructor (explicitly or implicitly): + + \code + QStringView sv1 = u"abc\0def"; // sv1 == "abc" + QStringView sv2 = u"abc\0def"_sv; // sv2 == "abc\0def" + \endcode + + \sa Qt::Literals::StringLiterals +*/ + QT_END_NAMESPACE diff --git a/src/corelib/text/qstringview.h b/src/corelib/text/qstringview.h index 454dd1fdad9..abf57e0ac8c 100644 --- a/src/corelib/text/qstringview.h +++ b/src/corelib/text/qstringview.h @@ -512,6 +512,17 @@ qsizetype QtPrivate::findString(QStringView str, qsizetype from, QChar ch, Qt::C return -1; } +namespace Qt { +inline namespace Literals { +inline namespace StringLiterals { +constexpr QStringView operator""_sv(const char16_t *str, size_t size) noexcept +{ + return QStringView(str, qsizetype(size)); +} +} // StringLiterals +} // Literals +} // Qt + QT_END_NAMESPACE #endif /* QSTRINGVIEW_H */ diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 4f78c8d7072..a86964122ab 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -6071,7 +6071,7 @@ QIcon QCommonStylePrivate::iconFromResourceTheme(QCommonStyle::StandardPixmap st addIconFiles(u"normalizedockup-", dockTitleIconSizes, icon); break; case QStyle::SP_ToolBarHorizontalExtensionButton: - addIconFiles(rtl(option) ? u"toolbar-ext-h-rtl-" : u"toolbar-ext-h-", toolBarExtHSizes, icon); + addIconFiles(rtl(option) ? u"toolbar-ext-h-rtl-"_sv : u"toolbar-ext-h-"_sv, toolBarExtHSizes, icon); break; case QStyle::SP_ToolBarVerticalExtensionButton: addIconFiles(u"toolbar-ext-v-", toolBarExtVSizes, icon); diff --git a/tests/auto/corelib/text/qstringview/tst_qstringview.cpp b/tests/auto/corelib/text/qstringview/tst_qstringview.cpp index 237abcc498d..aa470ac7991 100644 --- a/tests/auto/corelib/text/qstringview/tst_qstringview.cpp +++ b/tests/auto/corelib/text/qstringview/tst_qstringview.cpp @@ -287,6 +287,7 @@ private Q_SLOTS: void tokenize() const; void std_stringview_conversion(); + void userDefinedLiterals(); private: template @@ -974,5 +975,27 @@ void tst_QStringView::std_stringview_conversion() QCOMPARE(sv, std::u16string_view(u"Hello\0world\0", 12)); } +void tst_QStringView::userDefinedLiterals() +{ + using namespace Qt::StringLiterals; + auto sv = u"test"_sv; + static_assert(std::is_same_v); + + QCOMPARE(sv.size(), 4); + QCOMPARE(sv, "test"); + + sv = u""_sv; + QCOMPARE(sv.size(), 0); + QCOMPARE(sv, ""); + + sv = u"embedded\0nul"_sv; + QCOMPARE(sv.size(), 12); + QCOMPARE(sv, QStringView(u"embedded\0nul", 12)); + + constexpr auto csv = u"constexpr test"_sv; + static_assert(csv.size() == 14); + QCOMPARE(csv, "constexpr test"); +} + QTEST_APPLESS_MAIN(tst_QStringView) #include "tst_qstringview.moc"