QRegularExpression: introduce (global)matchView

QRegularExpression::match (and globalMatch) is currently overloaded
for QString and QStringView. This creates a subtle API asymmetry:

  QRegularExpression re;

  auto m1 = re.match(getQString());       // OK
  auto m2 = re.match(getStdU16String());  // Dangling

This goes against our decision that every time that there's a possible
lifetime issue at play, it should be "evident". Solving the lifetime
issue here is possible, but tricky -- since QRegularExpression
is out-of-line, one needs a type-erased container for the input
string (basically, std::any) to keep it alive and so on.

Instead I went for the simpler solution: deprecate match(QStringView)
and introduce matchView(QStringView) (same for globalMatch). This
makes it clear that the call is matching over a view and therefore
users are supposed to keep the source object alive.

Drive-by, remove the documentation that says that the QString
overloads might not keep the string alive: they do and forever will.

[ChangeLog][QtCore][QRegularExpression] Added the matchView()
and globalMatchView() functions that operate on string views.
The match(QStringView) and globalMatch(QStringView) overloads
have been deprecated.

Change-Id: I054b8605c2fdea59b556dcfea8920ef4eee78ee9
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Giuseppe D'Angelo 2022-07-05 19:01:08 +02:00
parent 8d6b274fa4
commit e540d4a864
4 changed files with 95 additions and 43 deletions

View File

@ -1588,11 +1588,6 @@ qsizetype QRegularExpression::patternErrorOffset() const
The returned QRegularExpressionMatch object contains the results of the
match.
\note The data referenced by \a subject should remain valid as long
as there are QRegularExpressionMatch objects using it. At the moment
Qt makes a (shallow) copy of the data, but this behavior may change
in a future version of Qt.
\sa QRegularExpressionMatch, {normal matching}
*/
QRegularExpressionMatch QRegularExpression::match(const QString &subject,
@ -1610,9 +1605,26 @@ QRegularExpressionMatch QRegularExpression::match(const QString &subject,
return QRegularExpressionMatch(*priv);
}
#if QT_DEPRECATED_SINCE(6, 8)
/*!
\since 6.0
\overload
\obsolete
Use matchView() instead.
*/
QRegularExpressionMatch QRegularExpression::match(QStringView subjectView,
qsizetype offset,
MatchType matchType,
MatchOptions matchOptions) const
{
return matchView(subjectView, offset, matchType, matchOptions);
}
#endif // QT_DEPRECATED_SINCE(6, 8)
/*!
\since 6.5
\overload
Attempts to match the regular expression against the given \a subjectView
string view, starting at the position \a offset inside the subject, using a
@ -1626,10 +1638,10 @@ QRegularExpressionMatch QRegularExpression::match(const QString &subject,
\sa QRegularExpressionMatch, {normal matching}
*/
QRegularExpressionMatch QRegularExpression::match(QStringView subjectView,
qsizetype offset,
MatchType matchType,
MatchOptions matchOptions) const
QRegularExpressionMatch QRegularExpression::matchView(QStringView subjectView,
qsizetype offset,
MatchType matchType,
MatchOptions matchOptions) const
{
d.data()->compilePattern();
auto priv = new QRegularExpressionMatchPrivate(*this,
@ -1650,11 +1662,6 @@ QRegularExpressionMatch QRegularExpression::match(QStringView subjectView,
The returned QRegularExpressionMatchIterator is positioned before the
first match result (if any).
\note The data referenced by \a subject should remain valid as long
as there are QRegularExpressionMatch objects using it. At the moment
Qt makes a (shallow) copy of the data, but this behavior may change
in a future version of Qt.
\sa QRegularExpressionMatchIterator, {global matching}
*/
QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QString &subject,
@ -1671,9 +1678,26 @@ QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QString &s
return QRegularExpressionMatchIterator(*priv);
}
#if QT_DEPRECATED_SINCE(6, 8)
/*!
\since 6.0
\overload
\obsolete
Use globalMatchView() instead.
*/
QRegularExpressionMatchIterator QRegularExpression::globalMatch(QStringView subjectView,
qsizetype offset,
MatchType matchType,
MatchOptions matchOptions) const
{
return globalMatchView(subjectView, offset, matchType, matchOptions);
}
#endif // QT_DEPRECATED_SINCE(6, 8)
/*!
\since 6.5
\overload
Attempts to perform a global match of the regular expression against the
given \a subjectView string view, starting at the position \a offset inside the
@ -1689,16 +1713,16 @@ QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QString &s
\sa QRegularExpressionMatchIterator, {global matching}
*/
QRegularExpressionMatchIterator QRegularExpression::globalMatch(QStringView subjectView,
qsizetype offset,
MatchType matchType,
MatchOptions matchOptions) const
QRegularExpressionMatchIterator QRegularExpression::globalMatchView(QStringView subjectView,
qsizetype offset,
MatchType matchType,
MatchOptions matchOptions) const
{
QRegularExpressionMatchIteratorPrivate *priv =
new QRegularExpressionMatchIteratorPrivate(*this,
matchType,
matchOptions,
match(subjectView, offset, matchType, matchOptions));
matchView(subjectView, offset, matchType, matchOptions));
return QRegularExpressionMatchIterator(*priv);
}

View File

@ -91,11 +91,20 @@ public:
MatchType matchType = NormalMatch,
MatchOptions matchOptions = NoMatchOption) const;
#if QT_DEPRECATED_SINCE(6, 8)
[[nodiscard]]
QT_DEPRECATED_VERSION_X_6_8("Use matchView instead.")
QRegularExpressionMatch match(QStringView subjectView,
qsizetype offset = 0,
MatchType matchType = NormalMatch,
MatchOptions matchOptions = NoMatchOption) const;
#endif
[[nodiscard]]
QRegularExpressionMatch matchView(QStringView subjectView,
qsizetype offset = 0,
MatchType matchType = NormalMatch,
MatchOptions matchOptions = NoMatchOption) const;
[[nodiscard]]
QRegularExpressionMatchIterator globalMatch(const QString &subject,
@ -103,11 +112,20 @@ public:
MatchType matchType = NormalMatch,
MatchOptions matchOptions = NoMatchOption) const;
#if QT_DEPRECATED_SINCE(6, 8)
[[nodiscard]]
QT_DEPRECATED_VERSION_X_6_8("Use globalMatchView instead.")
QRegularExpressionMatchIterator globalMatch(QStringView subjectView,
qsizetype offset = 0,
MatchType matchType = NormalMatch,
MatchOptions matchOptions = NoMatchOption) const;
#endif
[[nodiscard]]
QRegularExpressionMatchIterator globalMatchView(QStringView subjectView,
qsizetype offset = 0,
MatchType matchType = NormalMatch,
MatchOptions matchOptions = NoMatchOption) const;
void optimize() const;

View File

@ -7690,8 +7690,9 @@ QList<QStringView> QStringView::split(QChar sep, Qt::SplitBehavior behavior, Qt:
#if QT_CONFIG(regularexpression)
namespace {
template<class ResultList, typename String>
template<class ResultList, typename String, typename MatchingFunction>
static ResultList splitString(const String &source, const QRegularExpression &re,
MatchingFunction matchingFunction,
Qt::SplitBehavior behavior)
{
ResultList list;
@ -7702,7 +7703,7 @@ static ResultList splitString(const String &source, const QRegularExpression &re
qsizetype start = 0;
qsizetype end = 0;
QRegularExpressionMatchIterator iterator = re.globalMatch(source);
QRegularExpressionMatchIterator iterator = (re.*matchingFunction)(source, 0, QRegularExpression::NormalMatch, QRegularExpression::NoMatchOption);
while (iterator.hasNext()) {
QRegularExpressionMatch match = iterator.next();
end = match.capturedStart();
@ -7747,7 +7748,15 @@ static ResultList splitString(const String &source, const QRegularExpression &re
*/
QStringList QString::split(const QRegularExpression &re, Qt::SplitBehavior behavior) const
{
return splitString<QStringList>(*this, re, behavior);
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
const auto matchingFunction = qOverload<const QString &, qsizetype, QRegularExpression::MatchType, QRegularExpression::MatchOptions>(&QRegularExpression::globalMatch);
#else
const auto matchingFunction = &QRegularExpression::globalMatch;
#endif
return splitString<QStringList>(*this,
re,
matchingFunction,
behavior);
}
/*!
@ -7764,7 +7773,7 @@ QStringList QString::split(const QRegularExpression &re, Qt::SplitBehavior behav
*/
QList<QStringView> QStringView::split(const QRegularExpression &re, Qt::SplitBehavior behavior) const
{
return splitString<QList<QStringView>>(*this, re, behavior);
return splitString<QList<QStringView>>(*this, re, &QRegularExpression::globalMatchView, behavior);
}
#endif // QT_CONFIG(regularexpression)
@ -10793,7 +10802,7 @@ qsizetype QtPrivate::indexOf(QStringView viewHaystack, const QString *stringHays
QRegularExpressionMatch match = stringHaystack
? re.match(*stringHaystack, from)
: re.match(viewHaystack, from);
: re.matchView(viewHaystack, from);
if (match.hasMatch()) {
const qsizetype ret = match.capturedStart();
if (rmatch)
@ -10819,7 +10828,7 @@ qsizetype QtPrivate::lastIndexOf(QStringView viewHaystack, const QString *string
qsizetype endpos = (from < 0) ? (viewHaystack.size() + from + 1) : (from + 1);
QRegularExpressionMatchIterator iterator = stringHaystack
? re.globalMatch(*stringHaystack)
: re.globalMatch(viewHaystack);
: re.globalMatchView(viewHaystack);
qsizetype lastIndex = -1;
while (iterator.hasNext()) {
QRegularExpressionMatch match = iterator.next();
@ -10849,7 +10858,7 @@ bool QtPrivate::contains(QStringView viewHaystack, const QString *stringHaystack
}
QRegularExpressionMatch m = stringHaystack
? re.match(*stringHaystack)
: re.match(viewHaystack);
: re.matchView(viewHaystack);
bool hasMatch = m.hasMatch();
if (hasMatch && rmatch)
*rmatch = std::move(m);
@ -10871,7 +10880,7 @@ qsizetype QtPrivate::count(QStringView haystack, const QRegularExpression &re)
qsizetype index = -1;
qsizetype len = haystack.length();
while (index <= len - 1) {
QRegularExpressionMatch match = re.match(haystack, index + 1);
QRegularExpressionMatch match = re.matchView(haystack, index + 1);
if (!match.hasMatch())
break;
index = match.capturedStart();

View File

@ -393,6 +393,7 @@ static void testMatch(const QRegularExpression &regexp,
result);
}
// ### Qt 7: there should no longer be the need for these
typedef QRegularExpressionMatch (QRegularExpression::*QREMatchStringPMF)(const QString &, qsizetype, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const;
typedef QRegularExpressionMatch (QRegularExpression::*QREMatchStringViewPMF)(QStringView, qsizetype, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const;
typedef QRegularExpressionMatchIterator (QRegularExpression::*QREGlobalMatchStringPMF)(const QString &, qsizetype, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const;
@ -1096,7 +1097,7 @@ void tst_QRegularExpression::normalMatch()
testMatch<QRegularExpressionMatch>(regexp,
static_cast<QREMatchStringPMF>(&QRegularExpression::match),
static_cast<QREMatchStringViewPMF>(&QRegularExpression::match),
static_cast<QREMatchStringViewPMF>(&QRegularExpression::matchView),
subject,
offset,
QRegularExpression::NormalMatch,
@ -1368,7 +1369,7 @@ void tst_QRegularExpression::partialMatch()
testMatch<QRegularExpressionMatch>(regexp,
static_cast<QREMatchStringPMF>(&QRegularExpression::match),
static_cast<QREMatchStringViewPMF>(&QRegularExpression::match),
static_cast<QREMatchStringViewPMF>(&QRegularExpression::matchView),
subject,
offset,
matchType,
@ -1645,7 +1646,7 @@ void tst_QRegularExpression::globalMatch()
testMatch<QRegularExpressionMatchIterator>(regexp,
static_cast<QREGlobalMatchStringPMF>(&QRegularExpression::globalMatch),
static_cast<QREGlobalMatchStringViewPMF>(&QRegularExpression::globalMatch),
static_cast<QREGlobalMatchStringViewPMF>(&QRegularExpression::globalMatchView),
subject,
offset,
matchType,
@ -1985,7 +1986,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QCOMPARE(match.capturedEnd(), 4);
}
{
const QRegularExpressionMatch match = re.match(QStringView(subject));
const QRegularExpressionMatch match = re.matchView(QStringView(subject));
consistencyCheck(match);
QVERIFY(match.isValid());
QVERIFY(match.hasMatch());
@ -2003,7 +2004,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QCOMPARE(match.capturedEnd(), 4);
}
{
const QRegularExpressionMatch match = re.match(QStringView(subject), 1);
const QRegularExpressionMatch match = re.matchView(QStringView(subject), 1);
consistencyCheck(match);
QVERIFY(match.isValid());
QVERIFY(match.hasMatch());
@ -2021,7 +2022,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QCOMPARE(match.capturedEnd(), 6);
}
{
const QRegularExpressionMatch match = re.match(QStringView(subject).mid(1));
const QRegularExpressionMatch match = re.matchView(QStringView(subject).mid(1));
consistencyCheck(match);
QVERIFY(match.isValid());
QVERIFY(match.hasMatch());
@ -2039,7 +2040,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QCOMPARE(match.capturedEnd(), 6);
}
{
const QRegularExpressionMatch match = re.match(QStringView(subject).mid(1), 1);
const QRegularExpressionMatch match = re.matchView(QStringView(subject).mid(1), 1);
consistencyCheck(match);
QVERIFY(match.isValid());
QVERIFY(match.hasMatch());
@ -2057,7 +2058,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QCOMPARE(match.capturedEnd(), 7);
}
{
const QRegularExpressionMatch match = re.match(QStringView(subject), 4);
const QRegularExpressionMatch match = re.matchView(QStringView(subject), 4);
consistencyCheck(match);
QVERIFY(match.isValid());
QVERIFY(match.hasMatch());
@ -2072,7 +2073,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QVERIFY(!match.hasMatch());
}
{
const QRegularExpressionMatch match = re.match(QStringView(subject).mid(4));
const QRegularExpressionMatch match = re.matchView(QStringView(subject).mid(4));
consistencyCheck(match);
QVERIFY(match.isValid());
QVERIFY(!match.hasMatch());
@ -2105,7 +2106,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QVERIFY(!i.hasNext());
}
{
QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject));
QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject));
QVERIFY(i.isValid());
consistencyCheck(i);
@ -2157,7 +2158,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QVERIFY(!i.hasNext());
}
{
QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject), 1);
QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject), 1);
QVERIFY(i.isValid());
consistencyCheck(i);
@ -2199,7 +2200,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QVERIFY(!i.hasNext());
}
{
QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject).mid(1));
QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject).mid(1));
QVERIFY(i.isValid());
consistencyCheck(i);
@ -2231,7 +2232,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QVERIFY(!i.hasNext());
}
{
QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject).mid(1), 1);
QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject).mid(1), 1);
QVERIFY(i.isValid());
consistencyCheck(i);
@ -2263,7 +2264,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QVERIFY(!i.hasNext());
}
{
QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject).mid(1), 1);
QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject).mid(1), 1);
QVERIFY(i.isValid());
consistencyCheck(i);
@ -2296,7 +2297,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QVERIFY(!i.hasNext());
}
{
QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject), 4);
QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject), 4);
QVERIFY(i.isValid());
consistencyCheck(i);
@ -2318,7 +2319,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence()
QVERIFY(!i.hasNext());
}
{
QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject).mid(4));
QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject).mid(4));
consistencyCheck(i);
QVERIFY(i.isValid());
QVERIFY(!i.hasNext());