QStringBuilder: allow to be used with 'auto' keyword
The idea is to store a concatenable in a QStringBuilder object by value or by reference, depending on how it was originally passed into the concatenation operator. So if it was passed by r-value, we treat it as a temporary object and hold it by value (and use move-semantic if available), otherwise we hold it by reference (as before). To achieve this we first change concatenation operators '%' and '+' to take their arguments by universal reference. Next we instantiate QStringBuilder object with deduced types of the arguments, which will be a "value type" or a "reference type" according to "universal reference deduction rules". Further we use perfect forwarding to pass arguments to QStringBuilder's constructor. Thus arguments, initially passed by r-value reference and which are move-constructible, will be "moved" to corresponding QStringBuilder member variables. So, to summarize: 1. Arguments passed by l-value reference - stored in QStringBuilder object by reference (as before). 2. Temporary objects passed by r-value reference - stored in QStringBuilder object by value. If a type is move-constructible (QSting, QByteArray, etc), the object will be "moved" accordingly. Special thanks to Giuseppe D'Angelo for the tests. Fixes: QTBUG-99291 Fixes: QTBUG-87603 Fixes: QTBUG-47066 Task-number: QTBUG-74873 Task-number: QTBUG-103090 Task-number: QTBUG-104354 Change-Id: I64f417be0de0815ec5ae7e35a1cc6cef6d887933 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Vladimir Belyavsky <belyavskyv@gmail.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
parent
8d1304f4f2
commit
af8f9a2a6e
@ -30,7 +30,10 @@ protected:
|
||||
static void appendLatin1To(QLatin1StringView in, QChar *out) noexcept;
|
||||
};
|
||||
|
||||
template <typename T> struct QConcatenable {};
|
||||
template <typename T> struct QConcatenable;
|
||||
|
||||
template <typename T>
|
||||
using QConcatenableEx = QConcatenable<q20::remove_cvref_t<T>>;
|
||||
|
||||
namespace QtStringBuilder {
|
||||
template <typename A, typename B> struct ConvertToTypeHelper
|
||||
@ -72,10 +75,20 @@ struct QStringBuilderBase<Builder, QString> : public QStringBuilderCommon<Builde
|
||||
};
|
||||
|
||||
template <typename A, typename B>
|
||||
class QStringBuilder : public QStringBuilderBase<QStringBuilder<A, B>, typename QtStringBuilder::ConvertToTypeHelper<typename QConcatenable<A>::ConvertTo, typename QConcatenable<B>::ConvertTo>::ConvertTo>
|
||||
class QStringBuilder : public QStringBuilderBase<QStringBuilder<A, B>,
|
||||
typename QtStringBuilder::ConvertToTypeHelper<
|
||||
typename QConcatenableEx<A>::ConvertTo,
|
||||
typename QConcatenableEx<B>::ConvertTo
|
||||
>::ConvertTo
|
||||
>
|
||||
{
|
||||
public:
|
||||
QStringBuilder(const A &a_, const B &b_) : a(a_), b(b_) {}
|
||||
QStringBuilder(A &&a_, B &&b_) : a(std::forward<A>(a_)), b(std::forward<B>(b_)) {}
|
||||
|
||||
QStringBuilder(QStringBuilder &&) = default;
|
||||
QStringBuilder(const QStringBuilder &) = default;
|
||||
~QStringBuilder() = default;
|
||||
|
||||
private:
|
||||
friend class QByteArray;
|
||||
friend class QString;
|
||||
@ -116,8 +129,12 @@ public:
|
||||
return QtStringBuilder::isNull(a) && QtStringBuilder::isNull(b);
|
||||
}
|
||||
|
||||
const A &a;
|
||||
const B &b;
|
||||
A a;
|
||||
B b;
|
||||
|
||||
private:
|
||||
QStringBuilder &operator=(QStringBuilder &&) = delete;
|
||||
QStringBuilder &operator=(const QStringBuilder &) = delete;
|
||||
};
|
||||
|
||||
template <> struct QConcatenable<char> : private QAbstractConcatenable
|
||||
@ -361,34 +378,37 @@ template <typename A, typename B>
|
||||
struct QConcatenable< QStringBuilder<A, B> >
|
||||
{
|
||||
typedef QStringBuilder<A, B> type;
|
||||
typedef typename QtStringBuilder::ConvertToTypeHelper<typename QConcatenable<A>::ConvertTo, typename QConcatenable<B>::ConvertTo>::ConvertTo ConvertTo;
|
||||
enum { ExactSize = QConcatenable<A>::ExactSize && QConcatenable<B>::ExactSize };
|
||||
using ConvertTo = typename QtStringBuilder::ConvertToTypeHelper<
|
||||
typename QConcatenableEx<A>::ConvertTo,
|
||||
typename QConcatenableEx<B>::ConvertTo
|
||||
>::ConvertTo;
|
||||
enum { ExactSize = QConcatenableEx<A>::ExactSize && QConcatenableEx<B>::ExactSize };
|
||||
static qsizetype size(const type &p)
|
||||
{
|
||||
return QConcatenable<A>::size(p.a) + QConcatenable<B>::size(p.b);
|
||||
return QConcatenableEx<A>::size(p.a) + QConcatenableEx<B>::size(p.b);
|
||||
}
|
||||
template<typename T> static inline void appendTo(const type &p, T *&out)
|
||||
{
|
||||
QConcatenable<A>::appendTo(p.a, out);
|
||||
QConcatenable<B>::appendTo(p.b, out);
|
||||
QConcatenableEx<A>::appendTo(p.a, out);
|
||||
QConcatenableEx<B>::appendTo(p.b, out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename A, typename B>
|
||||
QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type>
|
||||
operator%(const A &a, const B &b)
|
||||
template <typename A, typename B,
|
||||
typename = std::void_t<typename QConcatenableEx<A>::type, typename QConcatenableEx<B>::type>>
|
||||
auto operator%(A &&a, B &&b)
|
||||
{
|
||||
return QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type>(a, b);
|
||||
return QStringBuilder<A, B>(std::forward<A>(a), std::forward<B>(b));
|
||||
}
|
||||
|
||||
// QT_USE_FAST_OPERATOR_PLUS was introduced in 4.7, QT_USE_QSTRINGBUILDER is to be used from 4.8 onwards
|
||||
// QT_USE_FAST_OPERATOR_PLUS does not remove the normal operator+ for QByteArray
|
||||
#if defined(QT_USE_FAST_OPERATOR_PLUS) || defined(QT_USE_QSTRINGBUILDER)
|
||||
template <typename A, typename B>
|
||||
QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type>
|
||||
operator+(const A &a, const B &b)
|
||||
template <typename A, typename B,
|
||||
typename = std::void_t<typename QConcatenableEx<A>::type, typename QConcatenableEx<B>::type>>
|
||||
auto operator+(A &&a, B &&b)
|
||||
{
|
||||
return QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type>(a, b);
|
||||
return std::forward<A>(a) % std::forward<B>(b);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -98,6 +98,152 @@ void checkNullVsEmpty(const String &empty)
|
||||
QVERIFY(result.isNull());
|
||||
}
|
||||
|
||||
namespace CheckAuto {
|
||||
// T is cvref-qualified, using universal reference deduction rules.
|
||||
template <typename T> struct Helper;
|
||||
|
||||
// These specializations forward to the non-const ones, and add const on top.
|
||||
template <typename T> struct Helper<const T>
|
||||
{
|
||||
static const T create() { return Helper<T>::create(); }
|
||||
static const T createNull() { return Helper<T>::createNull(); }
|
||||
};
|
||||
template <typename T> struct Helper<const T &>
|
||||
{
|
||||
static const T &create() { return Helper<T &>::create(); }
|
||||
static const T &createNull() { return Helper<T &>::createNull(); }
|
||||
};
|
||||
|
||||
template <> struct Helper<QString>
|
||||
{
|
||||
static QString create() { return QString::fromUtf8("QString rvalue"); }
|
||||
static QString createNull() { return QString(); }
|
||||
};
|
||||
|
||||
template <> struct Helper<QString &>
|
||||
{
|
||||
static QString &create() { static QString s = QString::fromUtf8("QString lvalue"); return s; }
|
||||
static QString &createNull() { static QString s; return s; }
|
||||
};
|
||||
|
||||
template <> struct Helper<QStringView>
|
||||
{
|
||||
static QStringView create() { return QStringView(u"QStringView rvalue"); }
|
||||
static QStringView createNull() { return QStringView(); }
|
||||
};
|
||||
|
||||
template <> struct Helper<QStringView &>
|
||||
{
|
||||
static QStringView &create() { static QStringView s = u"QStringView lvalue"; return s; }
|
||||
static QStringView &createNull() { static QStringView s; return s; }
|
||||
};
|
||||
|
||||
template <> struct Helper<QByteArray>
|
||||
{
|
||||
static QByteArray create() { return QByteArray("QByteArray rvalue"); }
|
||||
static QByteArray createNull() { return QByteArray(); }
|
||||
};
|
||||
|
||||
template <> struct Helper<QByteArray &>
|
||||
{
|
||||
static QByteArray &create() { static QByteArray ba = QByteArray("QByteArray lvalue"); return ba; }
|
||||
static QByteArray &createNull() { static QByteArray ba; return ba; }
|
||||
};
|
||||
|
||||
template <> struct Helper<QByteArrayView>
|
||||
{
|
||||
static QByteArrayView create() { return QByteArrayView("QByteArrayView rvalue"); }
|
||||
static QByteArrayView createNull() { return QByteArrayView(); }
|
||||
};
|
||||
|
||||
template <> struct Helper<QByteArrayView &>
|
||||
{
|
||||
static QByteArrayView &create() { static QByteArrayView ba = "QByteArrayView lvalue"; return ba; }
|
||||
static QByteArrayView &createNull() { static QByteArrayView ba; return ba; }
|
||||
};
|
||||
|
||||
template <> struct Helper<const char *>
|
||||
{
|
||||
static const char *create() { return "const char * rvalue"; }
|
||||
static const char *createNull() { return ""; }
|
||||
};
|
||||
|
||||
template <> struct Helper<const char *&>
|
||||
{
|
||||
static const char *&create() { static const char *s = "const char * lvalue"; return s; }
|
||||
static const char *&createNull() { static const char *s = ""; return s; }
|
||||
};
|
||||
|
||||
template <typename String1, typename String2, typename Result>
|
||||
void checkAutoImpl3()
|
||||
{
|
||||
{
|
||||
auto result = Helper<String1>::create() P Helper<String2>::create();
|
||||
Result expected = result;
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
{
|
||||
auto result = Helper<String2>::create() P Helper<String1>::create();
|
||||
Result expected = result;
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
{
|
||||
auto result = Helper<String1>::create() P Helper<String2>::create() P Helper<String1>::create();
|
||||
Result expected = result;
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
{
|
||||
auto result = Helper<String2>::create() P Helper<String1>::create() P Helper<String2>::create();
|
||||
Result expected = result;
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
{
|
||||
auto result = Helper<String1>::createNull() P Helper<String2>::create();
|
||||
Result expected = result;
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
{
|
||||
auto result = Helper<String1>::createNull() P Helper<String2>::createNull();
|
||||
Result expected = result;
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename String1, typename String2, typename Result>
|
||||
void checkAutoImpl2()
|
||||
{
|
||||
checkAutoImpl3<String1 , String2 , Result>();
|
||||
checkAutoImpl3<String1 &, String2 , Result>();
|
||||
checkAutoImpl3<String1 , String2 &, Result>();
|
||||
checkAutoImpl3<String1 &, String2 &, Result>();
|
||||
}
|
||||
|
||||
template <typename String1, typename String2, typename Result>
|
||||
void checkAutoImpl()
|
||||
{
|
||||
checkAutoImpl2< String1, String2, Result>();
|
||||
checkAutoImpl2<const String1, String2, Result>();
|
||||
checkAutoImpl2< String1, const String2, Result>();
|
||||
checkAutoImpl2<const String1, const String2, Result>();
|
||||
}
|
||||
|
||||
} // namespace CheckAuto
|
||||
|
||||
void checkAuto()
|
||||
{
|
||||
CheckAuto::checkAutoImpl<QString, QString, QString>();
|
||||
CheckAuto::checkAutoImpl<QString, QStringView, QString>();
|
||||
|
||||
CheckAuto::checkAutoImpl<QByteArray, QByteArray, QByteArray>();
|
||||
CheckAuto::checkAutoImpl<QByteArray, const char *, QByteArray>();
|
||||
CheckAuto::checkAutoImpl<QByteArray, QByteArrayView, QByteArray>();
|
||||
|
||||
#ifndef QT_NO_CAST_FROM_ASCII
|
||||
CheckAuto::checkAutoImpl<QString, const char *, QString>();
|
||||
CheckAuto::checkAutoImpl<QString, QByteArray, QString>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void runScenario()
|
||||
{
|
||||
// this code is latin1. TODO: replace it with the utf8 block below, once
|
||||
@ -381,6 +527,9 @@ void runScenario()
|
||||
checkNullVsEmpty(QStringLiteral(""));
|
||||
checkNullVsEmpty(QByteArrayLiteral(""));
|
||||
|
||||
// auto
|
||||
checkAuto();
|
||||
|
||||
checkItWorksWithFreeSpaceAtBegin(QByteArray(UTF8_LITERAL), "1234");
|
||||
if (QTest::currentTestFailed())
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user