From e1e6506c8dec6d0fc0fdbb15cfde43e7c0277403 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 31 Mar 2017 12:47:33 +0200 Subject: QString: add QStringView/QLatin1String overload of (non-multi) arg() Use the new overload directly in QXmlStream*. Saves 129B in QtCore text size on optimized GCC 6.1 Linux AMD64 builds, even though we added two more functions. [ChangeLog][QtCore][QString] Added arg(QStringView), arg(QLatin1String) overloads. Change-Id: Idf7236dcab763824593f34182e4e0b16b5ed4321 Reviewed-by: Anton Kudryavtsev Reviewed-by: Edward Welbourne --- src/corelib/doc/snippets/qstring/main.cpp | 11 +++ src/corelib/tools/qstring.cpp | 94 +++++++++++++++++++++--- src/corelib/tools/qstring.h | 6 ++ src/corelib/xml/qxmlstream.cpp | 8 +- tests/auto/corelib/tools/qstring/tst_qstring.cpp | 12 +-- 5 files changed, 109 insertions(+), 22 deletions(-) diff --git a/src/corelib/doc/snippets/qstring/main.cpp b/src/corelib/doc/snippets/qstring/main.cpp index dd65fad4e3..3a6f6483fb 100644 --- a/src/corelib/doc/snippets/qstring/main.cpp +++ b/src/corelib/doc/snippets/qstring/main.cpp @@ -262,6 +262,17 @@ void Widget::argFunction() .arg(i).arg(total).arg(fileName); //! [11] + { + //! [11] + int i; // current file's number + int total; // number of files to process + QStringView fileName; // current file's name + + QString status = QString("Processing file %1 of %2: %3") + .arg(i).arg(total).arg(fileName); + //! [11] + } + //! [12] //! [13] QString str; //! [12] diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 6c97e2563c..672dda804f 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -7363,10 +7363,10 @@ struct ArgEscapeData int escape_len; // total length of escape sequences which will be replaced }; -static ArgEscapeData findArgEscapes(const QString &s) +static ArgEscapeData findArgEscapes(QStringView s) { - const QChar *uc_begin = s.unicode(); - const QChar *uc_end = uc_begin + s.length(); + const QChar *uc_begin = s.begin(); + const QChar *uc_end = s.end(); ArgEscapeData d; @@ -7425,11 +7425,11 @@ static ArgEscapeData findArgEscapes(const QString &s) return d; } -static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int field_width, - const QString &arg, const QString &larg, QChar fillChar = QLatin1Char(' ')) +static QString replaceArgEscapes(QStringView s, const ArgEscapeData &d, int field_width, + QStringView arg, QStringView larg, QChar fillChar) { - const QChar *uc_begin = s.unicode(); - const QChar *uc_end = uc_begin + s.length(); + const QChar *uc_begin = s.begin(); + const QChar *uc_end = s.end(); int abs_field_width = qAbs(field_width); int result_len = s.length() @@ -7493,11 +7493,11 @@ static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int f } if (locale_arg) { - memcpy(rc, larg.unicode(), larg.length()*sizeof(QChar)); + memcpy(rc, larg.data(), larg.length()*sizeof(QChar)); rc += larg.length(); } else { - memcpy(rc, arg.unicode(), arg.length()*sizeof(QChar)); + memcpy(rc, arg.data(), arg.length()*sizeof(QChar)); rc += arg.length(); } @@ -7519,6 +7519,7 @@ static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int f return result; } +#if QT_STRINGVIEW_LEVEL < 2 /*! Returns a copy of this string with the lowest numbered place marker replaced by string \a a, i.e., \c %1, \c %2, ..., \c %99. @@ -7549,17 +7550,86 @@ static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int f in the range 1 to 99. */ QString QString::arg(const QString &a, int fieldWidth, QChar fillChar) const +{ + return arg(QStringView(a), fieldWidth, fillChar); +} +#endif // QT_STRINGVIEW_LEVEL < 2 + +/*! + \overload + \since 5.10 + + Returns a copy of this string with the lowest-numbered place-marker + replaced by string \a a, i.e., \c %1, \c %2, ..., \c %99. + + \a fieldWidth specifies the minimum amount of space that \a a + shall occupy. If \a a requires less space than \a fieldWidth, it + is padded to \a fieldWidth with character \a fillChar. A positive + \a fieldWidth produces right-aligned text. A negative \a fieldWidth + produces left-aligned text. + + This example shows how we might create a \c status string for + reporting progress while processing a list of files: + + \snippet qstring/main.cpp 11-qstringview + + First, \c arg(i) replaces \c %1. Then \c arg(total) replaces \c + %2. Finally, \c arg(fileName) replaces \c %3. + + One advantage of using arg() over asprintf() is that the order of the + numbered place markers can change, if the application's strings are + translated into other languages, but each arg() will still replace + the lowest-numbered unreplaced place-marker, no matter where it + appears. Also, if place-marker \c %i appears more than once in the + string, arg() replaces all of them. + + If there is no unreplaced place-marker remaining, a warning message + is printed and the result is undefined. Place-marker numbers must be + in the range 1 to 99. +*/ +QString QString::arg(QStringView a, int fieldWidth, QChar fillChar) const { ArgEscapeData d = findArgEscapes(*this); - if (d.occurrences == 0) { - qWarning("QString::arg: Argument missing: %s, %s", toLocal8Bit().data(), - a.toLocal8Bit().data()); + if (Q_UNLIKELY(d.occurrences == 0)) { + qWarning("QString::arg: Argument missing: %ls, %ls", qUtf16Printable(*this), + qUtf16Printable(a.toString())); return *this; } return replaceArgEscapes(*this, d, fieldWidth, a, a, fillChar); } +/*! + \overload + \since 5.10 + + Returns a copy of this string with the lowest-numbered place-marker + replaced by string \a a, i.e., \c %1, \c %2, ..., \c %99. + + \a fieldWidth specifies the minimum amount of space that \a a + shall occupy. If \a a requires less space than \a fieldWidth, it + is padded to \a fieldWidth with character \a fillChar. A positive + \a fieldWidth produces right-aligned text. A negative \a fieldWidth + produces left-aligned text. + + One advantage of using arg() over asprintf() is that the order of the + numbered place markers can change, if the application's strings are + translated into other languages, but each arg() will still replace + the lowest-numbered unreplaced place-marker, no matter where it + appears. Also, if place-marker \c %i appears more than once in the + string, arg() replaces all of them. + + If there is no unreplaced place-marker remaining, a warning message + is printed and the result is undefined. Place-marker numbers must be + in the range 1 to 99. +*/ +QString QString::arg(QLatin1String a, int fieldWidth, QChar fillChar) const +{ + QVarLengthArray utf16(a.size()); + qt_from_latin1(utf16.data(), a.data(), a.size()); + return arg(QStringView(utf16.data(), utf16.size()), fieldWidth, fillChar); +} + /*! \fn QString QString::arg(const QString& a1, const QString& a2) const \overload arg() diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 5110487889..87e88d74bd 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -319,8 +319,14 @@ public: QChar fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; QString arg(QChar a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; +#if QT_STRINGVIEW_LEVEL < 2 QString arg(const QString &a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; +#endif + QString arg(QStringView a, int fieldWidth = 0, + QChar fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; + QString arg(QLatin1String a, int fieldWidth = 0, + QChar fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; QString arg(const QString &a1, const QString &a2) const Q_REQUIRED_RESULT; QString arg(const QString &a1, const QString &a2, const QString &a3) const Q_REQUIRED_RESULT; QString arg(const QString &a1, const QString &a2, const QString &a3, diff --git a/src/corelib/xml/qxmlstream.cpp b/src/corelib/xml/qxmlstream.cpp index af35193b13..ecca5569f8 100644 --- a/src/corelib/xml/qxmlstream.cpp +++ b/src/corelib/xml/qxmlstream.cpp @@ -1558,7 +1558,7 @@ QStringRef QXmlStreamReaderPrivate::namespaceForPrefix(const QStringRef &prefix) #if 1 if (namespaceProcessing && !prefix.isEmpty()) - raiseWellFormedError(QXmlStream::tr("Namespace prefix '%1' not declared").arg(prefix.toString())); + raiseWellFormedError(QXmlStream::tr("Namespace prefix '%1' not declared").arg(prefix)); #endif return QStringRef(); @@ -1636,7 +1636,7 @@ void QXmlStreamReaderPrivate::resolveTag() if (attributes[j].name() == attribute.name() && attributes[j].namespaceUri() == attribute.namespaceUri() && (namespaceProcessing || attributes[j].qualifiedName() == attribute.qualifiedName())) - raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName().toString())); + raiseWellFormedError(QXmlStream::tr("Attribute '%1' redefined.").arg(attribute.qualifiedName())); } } @@ -1804,14 +1804,14 @@ void QXmlStreamReaderPrivate::startDocument() if(hasStandalone) err = QXmlStream::tr("The standalone pseudo attribute must appear after the encoding."); if(!QXmlUtils::isEncName(name)) - err = QXmlStream::tr("%1 is an invalid encoding name.").arg(name); + err = QXmlStream::tr("%1 is an invalid encoding name.").arg(value); else { #ifdef QT_NO_TEXTCODEC readBuffer = QString::fromLatin1(rawReadBuffer.data(), nbytesread); #else QTextCodec *const newCodec = QTextCodec::codecForName(name.toLatin1()); if (!newCodec) - err = QXmlStream::tr("Encoding %1 is unsupported").arg(name); + err = QXmlStream::tr("Encoding %1 is unsupported").arg(value); else if (newCodec != codec && !lockEncoding) { codec = newCodec; delete decoder; diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index 3f118d04ce..4a82d952ab 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -4726,8 +4726,8 @@ void tst_QString::arg() QString s14( "%1%2%3" ); QCOMPARE( s4.arg("foo"), QLatin1String("[foo]") ); - QCOMPARE( s5.arg("foo"), QLatin1String("[foo]") ); - QCOMPARE( s6.arg("foo"), QLatin1String("[foo]") ); + QCOMPARE( s5.arg(QLatin1String("foo")), QLatin1String("[foo]") ); + QCOMPARE( s6.arg(QStringViewLiteral("foo")), QLatin1String("[foo]") ); QCOMPARE( s7.arg("foo"), QLatin1String("[foo]") ); QCOMPARE( s8.arg("foo"), QLatin1String("[foo %1]") ); QCOMPARE( s8.arg("foo").arg("bar"), QLatin1String("[foo bar]") ); @@ -4788,11 +4788,11 @@ void tst_QString::arg() QCOMPARE( QString("%%%1%%%2").arg("foo").arg("bar"), QLatin1String("%%foo%%bar") ); QCOMPARE( QString("%1").arg("hello", -10), QLatin1String("hello ") ); - QCOMPARE( QString("%1").arg("hello", -5), QLatin1String("hello") ); - QCOMPARE( QString("%1").arg("hello", -2), QLatin1String("hello") ); + QCOMPARE( QString("%1").arg(QLatin1String("hello"), -5), QLatin1String("hello") ); + QCOMPARE( QString("%1").arg(QStringViewLiteral("hello"), -2), QLatin1String("hello") ); QCOMPARE( QString("%1").arg("hello", 0), QLatin1String("hello") ); - QCOMPARE( QString("%1").arg("hello", 2), QLatin1String("hello") ); - QCOMPARE( QString("%1").arg("hello", 5), QLatin1String("hello") ); + QCOMPARE( QString("%1").arg(QLatin1String("hello"), 2), QLatin1String("hello") ); + QCOMPARE( QString("%1").arg(QStringViewLiteral("hello"), 5), QLatin1String("hello") ); QCOMPARE( QString("%1").arg("hello", 10), QLatin1String(" hello") ); QCOMPARE( QString("%1%1").arg("hello"), QLatin1String("hellohello") ); QCOMPARE( QString("%2%1").arg("hello"), QLatin1String("%2hello") ); -- cgit v1.2.3