QString: towards QStringView::arg() pt.3: Long live QStringView/QLatin1String::arg()
This version of arg(), unlike its QString counterpart, transparently accepts views without conversion to QString, and is also extensible to further argument types, say a future QFormattedNumber. [ChangeLog][QtCore][QStringView/QLatin1String] Added arg(), taking arbitrarily many strings. Change-Id: If40ef3c445f63383e32573f3f515fdda84c7fe3a Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
6f84829031
commit
35431062bd
@ -8754,19 +8754,23 @@ QString QString::arg(double a, int fieldWidth, char fmt, int prec, QChar fillCha
|
||||
return replaceArgEscapes(*this, d, fieldWidth, arg, locale_arg, fillChar);
|
||||
}
|
||||
|
||||
static int getEscape(const QChar *uc, qsizetype *pos, qsizetype len, int maxNumber = 999)
|
||||
static inline ushort to_unicode(const QChar c) { return c.unicode(); }
|
||||
static inline ushort to_unicode(const char c) { return QLatin1Char{c}.unicode(); }
|
||||
|
||||
template <typename Char>
|
||||
static int getEscape(const Char *uc, qsizetype *pos, qsizetype len, int maxNumber = 999)
|
||||
{
|
||||
int i = *pos;
|
||||
++i;
|
||||
if (i < len && uc[i] == QLatin1Char('L'))
|
||||
++i;
|
||||
if (i < len) {
|
||||
int escape = uc[i].unicode() - '0';
|
||||
int escape = to_unicode(uc[i]) - '0';
|
||||
if (uint(escape) >= 10U)
|
||||
return -1;
|
||||
++i;
|
||||
while (i < len) {
|
||||
int digit = uc[i].unicode() - '0';
|
||||
int digit = to_unicode(uc[i]) - '0';
|
||||
if (uint(digit) >= 10U)
|
||||
break;
|
||||
escape = (escape * 10) + digit;
|
||||
@ -8817,18 +8821,23 @@ static int getEscape(const QChar *uc, qsizetype *pos, qsizetype len, int maxNumb
|
||||
namespace {
|
||||
struct Part
|
||||
{
|
||||
Q_DECL_CONSTEXPR Part() : string{}, number{0} {}
|
||||
Part() = default; // for QVarLengthArray; do not use
|
||||
Q_DECL_CONSTEXPR Part(QStringView s, int num = -1)
|
||||
: string{s}, number{num} {}
|
||||
: tag{QtPrivate::ArgBase::U16}, number{num}, data{s.utf16()}, size{s.size()} {}
|
||||
Q_DECL_CONSTEXPR Part(QLatin1String s, int num = -1)
|
||||
: tag{QtPrivate::ArgBase::L1}, number{num}, data{s.data()}, size{s.size()} {}
|
||||
|
||||
QStringView string;
|
||||
void reset(QStringView s) noexcept { *this = {s, number}; }
|
||||
void reset(QLatin1String s) noexcept { *this = {s, number}; }
|
||||
|
||||
QtPrivate::ArgBase::Tag tag;
|
||||
int number;
|
||||
const void *data;
|
||||
qsizetype size;
|
||||
};
|
||||
} // unnamed namespace
|
||||
|
||||
template <>
|
||||
class QTypeInfo<Part> : public QTypeInfoMerger<Part, QStringView, int> {}; // Q_DECLARE_METATYPE
|
||||
|
||||
Q_DECLARE_TYPEINFO(Part, Q_PRIMITIVE_TYPE);
|
||||
|
||||
namespace {
|
||||
|
||||
@ -8837,7 +8846,8 @@ enum { ExpectedParts = 32 };
|
||||
typedef QVarLengthArray<Part, ExpectedParts> ParseResult;
|
||||
typedef QVarLengthArray<int, ExpectedParts/2> ArgIndexToPlaceholderMap;
|
||||
|
||||
static ParseResult parseMultiArgFormatString(QStringView s)
|
||||
template <typename StringView>
|
||||
static ParseResult parseMultiArgFormatString(StringView s)
|
||||
{
|
||||
ParseResult result;
|
||||
|
||||
@ -8884,16 +8894,29 @@ static ArgIndexToPlaceholderMap makeArgIndexToPlaceholderMap(const ParseResult &
|
||||
return result;
|
||||
}
|
||||
|
||||
static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QString *args[])
|
||||
static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QtPrivate::ArgBase *args[])
|
||||
{
|
||||
using namespace QtPrivate;
|
||||
qsizetype totalSize = 0;
|
||||
for (Part &part : parts) {
|
||||
if (part.number != -1) {
|
||||
const auto it = std::find(argIndexToPlaceholderMap.begin(), argIndexToPlaceholderMap.end(), part.number);
|
||||
if (it != argIndexToPlaceholderMap.end())
|
||||
part.string = *args[it - argIndexToPlaceholderMap.begin()];
|
||||
if (it != argIndexToPlaceholderMap.end()) {
|
||||
const auto &arg = *args[it - argIndexToPlaceholderMap.begin()];
|
||||
switch (arg.tag) {
|
||||
case ArgBase::L1:
|
||||
part.reset(static_cast<const QLatin1StringArg&>(arg).string);
|
||||
break;
|
||||
case ArgBase::U8:
|
||||
Q_UNREACHABLE(); // waiting for QUtf8String...
|
||||
break;
|
||||
case ArgBase::U16:
|
||||
part.reset(static_cast<const QStringViewArg&>(arg).string);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
totalSize += part.string.size();
|
||||
totalSize += part.size;
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
@ -8901,18 +8924,35 @@ static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const A
|
||||
} // unnamed namespace
|
||||
|
||||
QString QString::multiArg(int numArgs, const QString **args) const
|
||||
{
|
||||
QVarLengthArray<QtPrivate::QStringViewArg, 9> sva;
|
||||
sva.reserve(numArgs);
|
||||
QVarLengthArray<const QtPrivate::ArgBase *, 9> pointers;
|
||||
pointers.reserve(numArgs);
|
||||
for (int i = 0; i < numArgs; ++i) {
|
||||
sva.push_back(QtPrivate::qStringLikeToArg(*args[i]));
|
||||
pointers.push_back(&sva.back());
|
||||
}
|
||||
return QtPrivate::argToQString(qToStringViewIgnoringNull(*this), static_cast<size_t>(numArgs), pointers.data());
|
||||
}
|
||||
|
||||
Q_ALWAYS_INLINE QString to_string(QLatin1String s) noexcept { return s; }
|
||||
Q_ALWAYS_INLINE QString to_string(QStringView s) noexcept { return s.toString(); }
|
||||
|
||||
template <typename StringView>
|
||||
static QString argToQStringImpl(StringView pattern, size_t numArgs, const QtPrivate::ArgBase **args)
|
||||
{
|
||||
// Step 1-2 above
|
||||
ParseResult parts = parseMultiArgFormatString(qToStringViewIgnoringNull(*this));
|
||||
ParseResult parts = parseMultiArgFormatString(pattern);
|
||||
|
||||
// 3-4
|
||||
ArgIndexToPlaceholderMap argIndexToPlaceholderMap = makeArgIndexToPlaceholderMap(parts);
|
||||
|
||||
if (argIndexToPlaceholderMap.size() > numArgs) // 3a
|
||||
argIndexToPlaceholderMap.resize(numArgs);
|
||||
else if (argIndexToPlaceholderMap.size() < numArgs) // 3b
|
||||
qWarning("QString::arg: %d argument(s) missing in %s",
|
||||
numArgs - argIndexToPlaceholderMap.size(), toLocal8Bit().data());
|
||||
if (static_cast<size_t>(argIndexToPlaceholderMap.size()) > numArgs) // 3a
|
||||
argIndexToPlaceholderMap.resize(int(numArgs));
|
||||
else if (Q_UNLIKELY(static_cast<size_t>(argIndexToPlaceholderMap.size()) < numArgs)) // 3b
|
||||
qWarning("QString::arg: %d argument(s) missing in %ls",
|
||||
int(numArgs - argIndexToPlaceholderMap.size()), qUtf16Printable(to_string(pattern)));
|
||||
|
||||
// 5
|
||||
const qsizetype totalSize = resolveStringRefsAndReturnTotalSize(parts, argIndexToPlaceholderMap, args);
|
||||
@ -8922,15 +8962,37 @@ QString QString::multiArg(int numArgs, const QString **args) const
|
||||
auto out = const_cast<QChar*>(result.constData());
|
||||
|
||||
for (Part part : parts) {
|
||||
if (const qsizetype sz = part.string.size()) {
|
||||
memcpy(out, part.string.data(), sz * sizeof(QChar));
|
||||
out += sz;
|
||||
switch (part.tag) {
|
||||
case QtPrivate::ArgBase::L1:
|
||||
if (part.size) {
|
||||
qt_from_latin1(reinterpret_cast<ushort*>(out),
|
||||
reinterpret_cast<const char*>(part.data), part.size);
|
||||
}
|
||||
break;
|
||||
case QtPrivate::ArgBase::U8:
|
||||
Q_UNREACHABLE(); // waiting for QUtf8String
|
||||
break;
|
||||
case QtPrivate::ArgBase::U16:
|
||||
if (part.size)
|
||||
memcpy(out, part.data, part.size * sizeof(QChar));
|
||||
break;
|
||||
}
|
||||
out += part.size;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString QtPrivate::argToQString(QStringView pattern, size_t n, const ArgBase **args)
|
||||
{
|
||||
return argToQStringImpl(pattern, n, args);
|
||||
}
|
||||
|
||||
QString QtPrivate::argToQString(QLatin1String pattern, size_t n, const ArgBase **args)
|
||||
{
|
||||
return argToQStringImpl(pattern, n, args);
|
||||
}
|
||||
|
||||
/*! \fn bool QString::isSimpleText() const
|
||||
|
||||
\internal
|
||||
|
@ -106,6 +106,9 @@ public:
|
||||
Q_DECL_CONSTEXPR bool isNull() const noexcept { return !data(); }
|
||||
Q_DECL_CONSTEXPR bool isEmpty() const noexcept { return !size(); }
|
||||
|
||||
template <typename...Args>
|
||||
Q_REQUIRED_RESULT inline QString arg(Args &&...args) const;
|
||||
|
||||
Q_DECL_CONSTEXPR QLatin1Char at(int i) const
|
||||
{ return Q_ASSERT(i >= 0), Q_ASSERT(i < size()), QLatin1Char(m_data[i]); }
|
||||
Q_DECL_CONSTEXPR QLatin1Char operator[](int i) const { return at(i); }
|
||||
@ -1979,6 +1982,58 @@ inline const QString &asString(const QString &s) { return s; }
|
||||
inline QString &&asString(QString &&s) { return std::move(s); }
|
||||
}
|
||||
|
||||
//
|
||||
// QStringView::arg() implementation
|
||||
//
|
||||
|
||||
namespace QtPrivate {
|
||||
|
||||
struct ArgBase {
|
||||
enum Tag : uchar { L1, U8, U16 } tag;
|
||||
};
|
||||
|
||||
struct QStringViewArg : ArgBase {
|
||||
QStringView string;
|
||||
QStringViewArg() = default;
|
||||
Q_DECL_CONSTEXPR explicit QStringViewArg(QStringView v) noexcept : ArgBase{U16}, string{v} {}
|
||||
};
|
||||
|
||||
struct QLatin1StringArg : ArgBase {
|
||||
QLatin1String string;
|
||||
QLatin1StringArg() = default;
|
||||
Q_DECL_CONSTEXPR explicit QLatin1StringArg(QLatin1String v) noexcept : ArgBase{L1}, string{v} {}
|
||||
};
|
||||
|
||||
Q_REQUIRED_RESULT Q_CORE_EXPORT QString argToQString(QStringView pattern, size_t n, const ArgBase **args);
|
||||
Q_REQUIRED_RESULT Q_CORE_EXPORT QString argToQString(QLatin1String pattern, size_t n, const ArgBase **args);
|
||||
|
||||
template <typename StringView, typename...Args>
|
||||
Q_REQUIRED_RESULT Q_ALWAYS_INLINE QString argToQStringDispatch(StringView pattern, const Args &...args)
|
||||
{
|
||||
const ArgBase *argBases[] = {&args..., /* avoid zero-sized array */ nullptr};
|
||||
return QtPrivate::argToQString(pattern, sizeof...(Args), argBases);
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline QStringViewArg qStringLikeToArg(QStringView s) noexcept { return QStringViewArg{s}; }
|
||||
inline QStringViewArg qStringLikeToArg(const QChar &c) noexcept { return QStringViewArg{QStringView{&c, 1}}; }
|
||||
Q_DECL_CONSTEXPR inline QLatin1StringArg qStringLikeToArg(QLatin1String s) noexcept { return QLatin1StringArg{s}; }
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
||||
template <typename...Args>
|
||||
Q_ALWAYS_INLINE
|
||||
QString QStringView::arg(Args &&...args) const
|
||||
{
|
||||
return QtPrivate::argToQStringDispatch(*this, QtPrivate::qStringLikeToArg(args)...);
|
||||
}
|
||||
|
||||
template <typename...Args>
|
||||
Q_ALWAYS_INLINE
|
||||
QString QLatin1String::arg(Args &&...args) const
|
||||
{
|
||||
return QtPrivate::argToQStringDispatch(*this, QtPrivate::qStringLikeToArg(args)...);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#if defined(QT_USE_FAST_OPERATOR_PLUS) || defined(QT_USE_QSTRINGBUILDER)
|
||||
|
@ -529,6 +529,24 @@ QT_BEGIN_NAMESPACE
|
||||
\sa operator[](), front(), back()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QString QStringView::arg(Args &&...args) const
|
||||
\fn QString QLatin1String::arg(Args &&...args) const
|
||||
\since 5.14
|
||||
|
||||
Replaces occurrences of \c{%N} in this string with the corresponding
|
||||
argument from \a args. The arguments are not positional: the first of
|
||||
the \a args replaces the \c{%N} with the lowest \c{N} (all of them), the
|
||||
second of the \a args the \c{%N} with the next-lowest \c{N} etc.
|
||||
|
||||
\c Args can consist of anything that implicitly converts to QStringView
|
||||
or QLatin1String.
|
||||
|
||||
In addition, the following types are also supported: QChar, QLatin1Char.
|
||||
|
||||
\sa QString::arg()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QChar QStringView::front() const
|
||||
|
||||
|
@ -226,6 +226,9 @@ public:
|
||||
// QString API
|
||||
//
|
||||
|
||||
template <typename...Args>
|
||||
Q_REQUIRED_RESULT inline QString arg(Args &&...args) const; // defined in qstring.h
|
||||
|
||||
Q_REQUIRED_RESULT QByteArray toLatin1() const { return QtPrivate::convertToLatin1(*this); }
|
||||
Q_REQUIRED_RESULT QByteArray toUtf8() const { return QtPrivate::convertToUtf8(*this); }
|
||||
Q_REQUIRED_RESULT QByteArray toLocal8Bit() const { return QtPrivate::convertToLocal8Bit(*this); }
|
||||
|
@ -45,6 +45,7 @@ class tst_QLatin1String : public QObject
|
||||
|
||||
private Q_SLOTS:
|
||||
void at();
|
||||
void arg() const;
|
||||
void midLeftRight();
|
||||
void nullString();
|
||||
void emptyString();
|
||||
@ -63,6 +64,47 @@ void tst_QLatin1String::at()
|
||||
QCOMPARE(l1[l1.size() - 1], QLatin1Char('d'));
|
||||
}
|
||||
|
||||
void tst_QLatin1String::arg() const
|
||||
{
|
||||
#define CHECK1(pattern, arg1, expected) \
|
||||
do { \
|
||||
auto p = QLatin1String(pattern); \
|
||||
QCOMPARE(p.arg(QLatin1String(arg1)), expected); \
|
||||
QCOMPARE(p.arg(QStringViewLiteral(arg1)), expected); \
|
||||
QCOMPARE(p.arg(QStringLiteral(arg1)), expected); \
|
||||
QCOMPARE(p.arg(QString(QLatin1String(arg1))), expected); \
|
||||
} while (false) \
|
||||
/*end*/
|
||||
#define CHECK2(pattern, arg1, arg2, expected) \
|
||||
do { \
|
||||
auto p = QLatin1String(pattern); \
|
||||
QCOMPARE(p.arg(QLatin1String(arg1), QLatin1String(arg2)), expected); \
|
||||
QCOMPARE(p.arg(QStringViewLiteral(arg1), QLatin1String(arg2)), expected); \
|
||||
QCOMPARE(p.arg(QLatin1String(arg1), QStringViewLiteral(arg2)), expected); \
|
||||
QCOMPARE(p.arg(QStringViewLiteral(arg1), QStringViewLiteral(arg2)), expected); \
|
||||
} while (false) \
|
||||
/*end*/
|
||||
|
||||
CHECK1("", "World", "");
|
||||
CHECK1("%1", "World", "World");
|
||||
CHECK1("!%1?", "World", "!World?");
|
||||
CHECK1("%1%1", "World", "WorldWorld");
|
||||
CHECK1("%1%2", "World", "World%2");
|
||||
CHECK1("%2%1", "World", "%2World");
|
||||
|
||||
CHECK2("", "Hello", "World", "");
|
||||
CHECK2("%1", "Hello", "World", "Hello");
|
||||
CHECK2("!%1, %2?", "Hello", "World", "!Hello, World?");
|
||||
CHECK2("%1%1", "Hello", "World", "HelloHello");
|
||||
CHECK2("%1%2", "Hello", "World", "HelloWorld");
|
||||
CHECK2("%2%1", "Hello", "World", "WorldHello");
|
||||
|
||||
#undef CHECK2
|
||||
#undef CHECK1
|
||||
|
||||
QCOMPARE(QLatin1String(" %2 %2 %1 %3 ").arg(QLatin1Char('c'), QChar::CarriageReturn, u'C'), " \r \r c C ");
|
||||
}
|
||||
|
||||
void tst_QLatin1String::midLeftRight()
|
||||
{
|
||||
const QLatin1String l1("Hello World");
|
||||
|
@ -134,6 +134,8 @@ private Q_SLOTS:
|
||||
void literals() const;
|
||||
void at() const;
|
||||
|
||||
void arg() const;
|
||||
|
||||
void fromQString() const;
|
||||
void fromQStringRef() const;
|
||||
|
||||
@ -425,6 +427,47 @@ void tst_QStringView::at() const
|
||||
QCOMPARE(sv.at(4), QChar('o')); QCOMPARE(sv[4], QChar('o'));
|
||||
}
|
||||
|
||||
void tst_QStringView::arg() const
|
||||
{
|
||||
#define CHECK1(pattern, arg1, expected) \
|
||||
do { \
|
||||
auto p = QStringViewLiteral(pattern); \
|
||||
QCOMPARE(p.arg(QLatin1String(arg1)), expected); \
|
||||
QCOMPARE(p.arg(QStringViewLiteral(arg1)), expected); \
|
||||
QCOMPARE(p.arg(QStringLiteral(arg1)), expected); \
|
||||
QCOMPARE(p.arg(QString(QLatin1String(arg1))), expected); \
|
||||
} while (false) \
|
||||
/*end*/
|
||||
#define CHECK2(pattern, arg1, arg2, expected) \
|
||||
do { \
|
||||
auto p = QStringViewLiteral(pattern); \
|
||||
QCOMPARE(p.arg(QLatin1String(arg1), QLatin1String(arg2)), expected); \
|
||||
QCOMPARE(p.arg(QStringViewLiteral(arg1), QLatin1String(arg2)), expected); \
|
||||
QCOMPARE(p.arg(QLatin1String(arg1), QStringViewLiteral(arg2)), expected); \
|
||||
QCOMPARE(p.arg(QStringViewLiteral(arg1), QStringViewLiteral(arg2)), expected); \
|
||||
} while (false) \
|
||||
/*end*/
|
||||
|
||||
CHECK1("", "World", "");
|
||||
CHECK1("%1", "World", "World");
|
||||
CHECK1("!%1?", "World", "!World?");
|
||||
CHECK1("%1%1", "World", "WorldWorld");
|
||||
CHECK1("%1%2", "World", "World%2");
|
||||
CHECK1("%2%1", "World", "%2World");
|
||||
|
||||
CHECK2("", "Hello", "World", "");
|
||||
CHECK2("%1", "Hello", "World", "Hello");
|
||||
CHECK2("!%1, %2?", "Hello", "World", "!Hello, World?");
|
||||
CHECK2("%1%1", "Hello", "World", "HelloHello");
|
||||
CHECK2("%1%2", "Hello", "World", "HelloWorld");
|
||||
CHECK2("%2%1", "Hello", "World", "WorldHello");
|
||||
|
||||
#undef CHECK2
|
||||
#undef CHECK1
|
||||
|
||||
QCOMPARE(QStringViewLiteral(" %2 %2 %1 %3 ").arg(QLatin1Char('c'), QChar::CarriageReturn, u'C'), " \r \r c C ");
|
||||
}
|
||||
|
||||
void tst_QStringView::fromQString() const
|
||||
{
|
||||
QString null;
|
||||
|
Loading…
x
Reference in New Issue
Block a user