/**************************************************************************** ** ** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include template using CanConvert = std::is_convertible; Q_STATIC_ASSERT(!CanConvert::value); Q_STATIC_ASSERT(!CanConvert::value); Q_STATIC_ASSERT(!CanConvert::value); // QStringView qchar_does_not_compile() { return QStringView(QChar('a')); } // QStringView qlatin1string_does_not_compile() { return QStringView(QLatin1String("a")); } // QStringView const_char_star_does_not_compile() { return QStringView("a"); } // QStringView qbytearray_does_not_compile() { return QStringView(QByteArray("a")); } // // QChar // Q_STATIC_ASSERT(!CanConvert::value); Q_STATIC_ASSERT(CanConvert::value); Q_STATIC_ASSERT(CanConvert< QString >::value); Q_STATIC_ASSERT(CanConvert::value); Q_STATIC_ASSERT(CanConvert< QString&>::value); Q_STATIC_ASSERT(CanConvert::value); Q_STATIC_ASSERT(CanConvert< QStringRef >::value); Q_STATIC_ASSERT(CanConvert::value); Q_STATIC_ASSERT(CanConvert< QStringRef&>::value); Q_STATIC_ASSERT(CanConvert::value); // // ushort // Q_STATIC_ASSERT(!CanConvert::value); Q_STATIC_ASSERT(CanConvert::value); Q_STATIC_ASSERT(CanConvert< ushort*>::value); Q_STATIC_ASSERT(CanConvert::value); // // char16_t // #if defined(Q_COMPILER_UNICODE_STRINGS) Q_STATIC_ASSERT(!CanConvert::value); Q_STATIC_ASSERT(CanConvert< char16_t*>::value); Q_STATIC_ASSERT(CanConvert::value); #endif #if defined(Q_STDLIB_UNICODE_STRINGS) Q_STATIC_ASSERT(CanConvert< std::u16string >::value); Q_STATIC_ASSERT(CanConvert::value); Q_STATIC_ASSERT(CanConvert< std::u16string&>::value); Q_STATIC_ASSERT(CanConvert::value); #endif // // wchar_t // Q_CONSTEXPR bool CanConvertFromWCharT = #ifdef Q_OS_WIN true #else false #endif ; Q_STATIC_ASSERT(!CanConvert::value); Q_STATIC_ASSERT(CanConvert< wchar_t*>::value == CanConvertFromWCharT); Q_STATIC_ASSERT(CanConvert::value == CanConvertFromWCharT); Q_STATIC_ASSERT(CanConvert< std::wstring >::value == CanConvertFromWCharT); Q_STATIC_ASSERT(CanConvert::value == CanConvertFromWCharT); Q_STATIC_ASSERT(CanConvert< std::wstring&>::value == CanConvertFromWCharT); Q_STATIC_ASSERT(CanConvert::value == CanConvertFromWCharT); class tst_QStringView : public QObject { Q_OBJECT private Q_SLOTS: void constExpr() const; void basics() const; void literals() const; void at() const; void fromQString() const; void fromQStringRef() const; void fromQCharStar() const { const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; fromLiteral(str); } void fromUShortStar() const { const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; fromLiteral(str); } void fromChar16TStar() const { #if defined(Q_COMPILER_UNICODE_STRINGS) fromLiteral(u"Hello, World!"); #else QSKIP("This test requires C++11 char16_t support enabled in the compiler"); #endif } void fromWCharTStar() const { #ifdef Q_OS_WIN fromLiteral(L"Hello, World!"); #else QSKIP("This is a Windows-only test"); #endif } void fromQCharRange() const { const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; fromRange(std::begin(str), std::end(str)); } void fromUShortRange() const { const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; fromRange(std::begin(str), std::end(str)); } void fromChar16TRange() const { #if defined(Q_COMPILER_UNICODE_STRINGS) const char16_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; fromRange(std::begin(str), std::end(str)); #else QSKIP("This test requires C++11 char16_t support enabled in the compiler"); #endif } void fromWCharTRange() const { #ifdef Q_OS_WIN const wchar_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; fromRange(std::begin(str), std::end(str)); #else QSKIP("This is a Windows-only test"); #endif } // std::basic_string void fromStdStringWCharT() const { #ifdef Q_OS_WIN fromStdString(); #else QSKIP("This is a Windows-only test"); #endif } void fromStdStringChar16T() const { #ifdef Q_STDLIB_UNICODE_STRINGS fromStdString(); #else QSKIP("This test requires C++11 char16_t support enabled in compiler & stdlib"); #endif } private: template void conversion_tests(String arg) const; template void fromLiteral(const Char *arg) const; template void fromRange(const Char *first, const Char *last) const; template void fromContainer() const; template void fromStdString() const { fromContainer >(); } }; void tst_QStringView::constExpr() const { // compile-time checks #ifdef Q_COMPILER_CONSTEXPR { constexpr QStringView sv; Q_STATIC_ASSERT(sv.size() == 0); Q_STATIC_ASSERT(sv.isNull()); Q_STATIC_ASSERT(sv.empty()); Q_STATIC_ASSERT(sv.isEmpty()); Q_STATIC_ASSERT(sv.utf16() == nullptr); constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); Q_STATIC_ASSERT(sv2.isNull()); Q_STATIC_ASSERT(sv2.empty()); } { constexpr QStringView sv = QStringViewLiteral(""); Q_STATIC_ASSERT(sv.size() == 0); Q_STATIC_ASSERT(!sv.isNull()); Q_STATIC_ASSERT(sv.empty()); Q_STATIC_ASSERT(sv.isEmpty()); Q_STATIC_ASSERT(sv.utf16() != nullptr); constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); Q_STATIC_ASSERT(!sv2.isNull()); Q_STATIC_ASSERT(sv2.empty()); } { constexpr QStringView sv = QStringViewLiteral("Hello"); Q_STATIC_ASSERT(sv.size() == 5); Q_STATIC_ASSERT(!sv.empty()); Q_STATIC_ASSERT(!sv.isEmpty()); Q_STATIC_ASSERT(!sv.isNull()); Q_STATIC_ASSERT(*sv.utf16() == 'H'); Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); Q_STATIC_ASSERT(!sv2.isNull()); Q_STATIC_ASSERT(!sv2.empty()); Q_STATIC_ASSERT(sv2.size() == 5); } #if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS) { Q_STATIC_ASSERT(QStringView(u"Hello").size() == 5); constexpr QStringView sv = u"Hello"; Q_STATIC_ASSERT(sv.size() == 5); Q_STATIC_ASSERT(!sv.empty()); Q_STATIC_ASSERT(!sv.isEmpty()); Q_STATIC_ASSERT(!sv.isNull()); Q_STATIC_ASSERT(*sv.utf16() == 'H'); Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); Q_STATIC_ASSERT(!sv2.isNull()); Q_STATIC_ASSERT(!sv2.empty()); Q_STATIC_ASSERT(sv2.size() == 5); constexpr char16_t *null = nullptr; constexpr QStringView sv3(null); Q_STATIC_ASSERT(sv3.isNull()); Q_STATIC_ASSERT(sv3.isEmpty()); Q_STATIC_ASSERT(sv3.size() == 0); } #else // storage_type is wchar_t { Q_STATIC_ASSERT(QStringView(L"Hello").size() == 5); constexpr QStringView sv = L"Hello"; Q_STATIC_ASSERT(sv.size() == 5); Q_STATIC_ASSERT(!sv.empty()); Q_STATIC_ASSERT(!sv.isEmpty()); Q_STATIC_ASSERT(!sv.isNull()); Q_STATIC_ASSERT(*sv.utf16() == 'H'); Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); Q_STATIC_ASSERT(!sv2.isNull()); Q_STATIC_ASSERT(!sv2.empty()); Q_STATIC_ASSERT(sv2.size() == 5); constexpr wchar_t *null = nullptr; constexpr QStringView sv3(null); Q_STATIC_ASSERT(sv3.isNull()); Q_STATIC_ASSERT(sv3.isEmpty()); Q_STATIC_ASSERT(sv3.size() == 0); } #endif #endif } void tst_QStringView::basics() const { QStringView sv1; // a default-constructed QStringView is null: QVERIFY(sv1.isNull()); // which implies it's empty(); QVERIFY(sv1.isEmpty()); QStringView sv2; QVERIFY(sv2 == sv1); QVERIFY(!(sv2 != sv1)); } void tst_QStringView::literals() const { #if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS) const char16_t hello[] = u"Hello"; const char16_t longhello[] = u"Hello World. This is a much longer message, to exercise qustrlen."; const char16_t withnull[] = u"a\0zzz"; #else // storage_type is wchar_t const wchar_t hello[] = L"Hello"; const wchar_t longhello[] = L"Hello World. This is a much longer message, to exercise qustrlen."; const wchar_t withnull[] = L"a\0zzz"; #endif Q_STATIC_ASSERT(sizeof(longhello) >= 16); QCOMPARE(QStringView(hello).size(), 5); QCOMPARE(QStringView(hello + 0).size(), 5); // forces decay to pointer QStringView sv = hello; QCOMPARE(sv.size(), 5); QVERIFY(!sv.empty()); QVERIFY(!sv.isEmpty()); QVERIFY(!sv.isNull()); QCOMPARE(*sv.utf16(), 'H'); QCOMPARE(sv[0], QLatin1Char('H')); QCOMPARE(sv.at(0), QLatin1Char('H')); QCOMPARE(sv.front(), QLatin1Char('H')); QCOMPARE(sv.first(), QLatin1Char('H')); QCOMPARE(sv[4], QLatin1Char('o')); QCOMPARE(sv.at(4), QLatin1Char('o')); QCOMPARE(sv.back(), QLatin1Char('o')); QCOMPARE(sv.last(), QLatin1Char('o')); QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); QVERIFY(!sv2.isNull()); QVERIFY(!sv2.empty()); QCOMPARE(sv2.size(), 5); QStringView sv3(longhello); QCOMPARE(size_t(sv3.size()), sizeof(longhello)/sizeof(longhello[0]) - 1); QCOMPARE(sv3.last(), QLatin1Char('.')); sv3 = longhello; QCOMPARE(size_t(sv3.size()), sizeof(longhello)/sizeof(longhello[0]) - 1); for (int i = 0; i < sv3.size(); ++i) { QStringView sv4(longhello + i); QCOMPARE(size_t(sv4.size()), sizeof(longhello)/sizeof(longhello[0]) - 1 - i); QCOMPARE(sv4.last(), QLatin1Char('.')); sv4 = longhello + i; QCOMPARE(size_t(sv4.size()), sizeof(longhello)/sizeof(longhello[0]) - 1 - i); } // these are different results QCOMPARE(size_t(QStringView(withnull).size()), sizeof(withnull)/sizeof(withnull[0]) - 1); QCOMPARE(QStringView(withnull + 0).size(), 1); } void tst_QStringView::at() const { QString hello("Hello"); QStringView sv(hello); QCOMPARE(sv.at(0), QChar('H')); QCOMPARE(sv[0], QChar('H')); QCOMPARE(sv.at(1), QChar('e')); QCOMPARE(sv[1], QChar('e')); QCOMPARE(sv.at(2), QChar('l')); QCOMPARE(sv[2], QChar('l')); QCOMPARE(sv.at(3), QChar('l')); QCOMPARE(sv[3], QChar('l')); QCOMPARE(sv.at(4), QChar('o')); QCOMPARE(sv[4], QChar('o')); } void tst_QStringView::fromQString() const { QString null; QString empty = ""; QVERIFY( QStringView(null).isNull()); QVERIFY( QStringView(null).isEmpty()); QVERIFY( QStringView(empty).isEmpty()); QVERIFY(!QStringView(empty).isNull()); conversion_tests(QString("Hello World!")); } void tst_QStringView::fromQStringRef() const { QStringRef null; QString emptyS = ""; QStringRef empty(&emptyS); QVERIFY( QStringView(null).isNull()); QVERIFY( QStringView(null).isEmpty()); QVERIFY( QStringView(empty).isEmpty()); QVERIFY(!QStringView(empty).isNull()); conversion_tests(QString("Hello World!").midRef(6)); } template void tst_QStringView::fromLiteral(const Char *arg) const { const Char *null = nullptr; const Char empty[] = { 0 }; QCOMPARE(QStringView(null).size(), qsizetype(0)); QCOMPARE(QStringView(null).data(), nullptr); QCOMPARE(QStringView(empty).size(), qsizetype(0)); QCOMPARE(static_cast(QStringView(empty).data()), static_cast(empty)); QVERIFY( QStringView(null).isNull()); QVERIFY( QStringView(null).isEmpty()); QVERIFY( QStringView(empty).isEmpty()); QVERIFY(!QStringView(empty).isNull()); conversion_tests(arg); } template void tst_QStringView::fromRange(const Char *first, const Char *last) const { const Char *null = nullptr; QCOMPARE(QStringView(null, null).size(), 0); QCOMPARE(QStringView(null, null).data(), nullptr); QCOMPARE(QStringView(first, first).size(), 0); QCOMPARE(static_cast(QStringView(first, first).data()), static_cast(first)); const auto sv = QStringView(first, last); QCOMPARE(sv.size(), last - first); QCOMPARE(static_cast(sv.data()), static_cast(first)); // can't call conversion_tests() here, as it requires a single object } template void tst_QStringView::fromContainer() const { const QString s = "Hello World!"; Container c; // unspecified whether empty containers make null QStringViews QVERIFY(QStringView(c).isEmpty()); QCOMPARE(sizeof(Char), sizeof(QChar)); const auto *data = reinterpret_cast(s.utf16()); std::copy(data, data + s.size(), std::back_inserter(c)); conversion_tests(std::move(c)); } namespace help { template size_t size(const T &t) { return size_t(t.size()); } template size_t size(const T *t) { size_t result = 0; if (t) { while (*t++) ++result; } return result; } size_t size(const QChar *t) { size_t result = 0; if (t) { while (!t++->isNull()) ++result; } return result; } template typename T::const_iterator cbegin(const T &t) { return t.cbegin(); } template const T * cbegin(const T *t) { return t; } template typename T::const_iterator cend(const T &t) { return t.cend(); } template const T * cend(const T *t) { return t + size(t); } template typename T::const_reverse_iterator crbegin(const T &t) { return t.crbegin(); } template std::reverse_iterator crbegin(const T *t) { return std::reverse_iterator(cend(t)); } template typename T::const_reverse_iterator crend(const T &t) { return t.crend(); } template std::reverse_iterator crend(const T *t) { return std::reverse_iterator(cbegin(t)); } } // namespace help template void tst_QStringView::conversion_tests(String string) const { // copy-construct: { QStringView sv = string; QCOMPARE(help::size(sv), help::size(string)); // check iterators: QVERIFY(std::equal(help::cbegin(string), help::cend(string), QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.cbegin(), sv.size()))); QVERIFY(std::equal(help::cbegin(string), help::cend(string), QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.begin(), sv.size()))); QVERIFY(std::equal(help::crbegin(string), help::crend(string), sv.crbegin())); QVERIFY(std::equal(help::crbegin(string), help::crend(string), sv.rbegin())); QCOMPARE(sv, string); } QStringView sv; // copy-assign: { sv = string; QCOMPARE(help::size(sv), help::size(string)); // check relational operators: QCOMPARE(sv, string); QCOMPARE(string, sv); QVERIFY(!(sv != string)); QVERIFY(!(string != sv)); QVERIFY(!(sv < string)); QVERIFY(sv <= string); QVERIFY(!(sv > string)); QVERIFY(sv >= string); QVERIFY(!(string < sv)); QVERIFY(string <= sv); QVERIFY(!(string > sv)); QVERIFY(string >= sv); } // copy-construct from rvalue (QStringView never assumes ownership): { QStringView sv2 = std::move(string); QCOMPARE(sv2, sv); QCOMPARE(sv2, string); } // copy-assign from rvalue (QStringView never assumes ownership): { QStringView sv2; sv2 = std::move(string); QCOMPARE(sv2, sv); QCOMPARE(sv2, string); } } QTEST_APPLESS_MAIN(tst_QStringView) #include "tst_qstringview.moc"