QByteArrayView: move array size deduction feature to fromArray

1. Make the ctor unable to construct a QByteArrayView from
array literals other than 'char'.
    With the new behavior it would either be (very likely) unintended to
    pass e.g. a std::byte array to the ctor. And it would be confusing
    because you would get different sizes based on signed-ness.
2. Introduce fromArray
    Only supports array literals. Constructs a view of the full size.
    Explicit so it shouldn't be surprising.

Change-Id: Ifdb55eb21057dfe7053b2561bd81e2c9825e9bc6
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
bb10
Mårten Nordheim 2020-09-14 15:26:49 +02:00
parent 7a5e0c5712
commit bbe7570ddc
4 changed files with 110 additions and 48 deletions

View File

@ -58,10 +58,3 @@
void fun(QByteArrayView bv);
void fun(char ch) { fun(QByteArrayView(&ch, 1)); }
//! [1]
void wrapper() {
const char array[1] = { 0 };
//! [2]
auto bv = QByteArrayView(array, std::size(array)); // using C++17 std::size()
//! [2]
}

View File

@ -94,6 +94,9 @@ struct IsContainerCompatibleWithQByteArrayView<T, std::enable_if_t<
// This needs to be treated specially due to the empty vs null distinction
std::negation<std::is_same<std::decay_t<T>, QByteArray>>,
// We handle array literals specially for source compat reasons
std::negation<std::is_array<T>>,
// Don't make an accidental copy constructor
std::negation<std::is_same<std::decay_t<T>, QByteArrayView>>>>> : std::true_type {};
@ -147,10 +150,11 @@ private:
return qsizetype(std::size(c));
}
template <typename Char, size_t N>
static constexpr qsizetype lengthHelperContainer(const Char (&)[N]) noexcept
static constexpr qsizetype lengthHelperCharArray(const char *data, size_t size) noexcept
{
return qsizetype(N - 1);
const auto it = std::char_traits<char>::find(data, size, '\0');
const auto end = it ? it : std::next(data, size);
return qsizetype(std::distance(data, end));
}
template <typename Byte>
@ -175,9 +179,6 @@ public:
: QByteArrayView(first, last - first) {}
#ifdef Q_QDOC
template <typename Byte, size_t N>
constexpr QByteArrayView(const Byte (&array)[N]) noexcept;
template <typename Byte>
constexpr QByteArrayView(const Byte *data) noexcept;
#else
@ -198,6 +199,13 @@ public:
template <typename Container, if_compatible_container<Container> = true>
constexpr QByteArrayView(const Container &c) noexcept
: QByteArrayView(std::data(c), lengthHelperContainer(c)) {}
template <size_t Size>
constexpr QByteArrayView(const char (&data)[Size]) noexcept
: QByteArrayView(data, lengthHelperCharArray(data, Size)) {}
template <typename Byte, size_t Size, if_compatible_byte<Byte> = true>
[[nodiscard]] constexpr static QByteArrayView fromArray(const Byte (&data)[Size]) noexcept
{ return QByteArrayView(data, Size); }
[[nodiscard]] inline QByteArray toByteArray() const; // defined in qbytearray.h

View File

@ -256,23 +256,25 @@
*/
/*!
\fn template <typename Byte, size_t N> QByteArrayView::QByteArrayView(const Byte (&data)[N])
\fn template <size_t Size> QByteArrayView(const char (&data)[Size])
Constructs a byte array view on the array of bytes \a data.
The length is set to \c{N-1}, excluding the trailing \{Byte(0)}.
If you need the full array, use the constructor from pointer and
size instead:
\snippet code/src_corelib_text_qbytearrayview.cpp 2
Constructs a byte array view on the char array \a data.
The view covers the array until the first \c{'\0'} is encountered,
or \c Size, whichever comes first.
If you need the full array, use fromArray() instead.
\a data must remain valid for the lifetime of this byte array view
object.
This constructor only participates in overload resolution if \a
data is an actual array and \c Byte is a compatible byte
type.
\note This constructor is only available for char array literals.
The reasoning behind that is for compatibility with C-libraries
which predefine "large-enough" arrays, but only use some of the
preallocated space. To support this in an intuitive way in an
implicit constructor overload, we need to stop at the first
\c{char(0)}. This is logical for a char array, but not
for a \c{std::byte} array.
\sa {Compatible Byte Types}
\sa fromArray
*/
/*!
@ -299,6 +301,21 @@
\sa {Compatible Byte Types}
*/
/*!
\fn template <typename Byte, size_t Size> static QByteArrayView QByteArrayView::fromArray(Byte (&data)[Size])
Constructs a byte array view on the array literal \a data. The view covers the full
array. That includes the trailing null-terminator of \c{char} array literals.
If you don't want the null-terminator included in the view, you can chop() it off
when you are certain it is at the end. Alternatively you can use the constructor
overload taking a char array literal which will create a view up to, but not including,
the first null-terminator in the data.
This function will work with any array literal of a compatible byte type.
\sa {Compatible Byte Types}, QByteArrayView
*/
/*!
\fn QByteArray QByteArrayView::toByteArray() const

View File

@ -48,20 +48,20 @@ static_assert(CanConvert<char*>);
static_assert(CanConvert<const char*>);
static_assert(!CanConvert<uchar>);
static_assert(CanConvert<uchar[1]>);
static_assert(CanConvert<const uchar[1]>);
static_assert(!CanConvert<uchar[1]>);
static_assert(!CanConvert<const uchar[1]>);
static_assert(CanConvert<uchar*>);
static_assert(CanConvert<const uchar*>);
static_assert(!CanConvert<signed char>);
static_assert(CanConvert<signed char[1]>);
static_assert(CanConvert<const signed char[1]>);
static_assert(!CanConvert<signed char[1]>);
static_assert(!CanConvert<const signed char[1]>);
static_assert(CanConvert<signed char*>);
static_assert(CanConvert<const signed char*>);
static_assert(!CanConvert<std::byte>);
static_assert(CanConvert<std::byte[1]>);
static_assert(CanConvert<const std::byte[1]>);
static_assert(!CanConvert<std::byte[1]>);
static_assert(!CanConvert<const std::byte[1]>);
static_assert(CanConvert<std::byte*>);
static_assert(CanConvert<const std::byte*>);
@ -110,6 +110,7 @@ private slots:
void constExpr() const;
void basics() const;
void literals() const;
void fromArray() const;
void literalsWithInternalNulls() const;
void at() const;
@ -324,9 +325,11 @@ void tst_QByteArrayView::basics() const
QVERIFY(!(bv2 != bv1));
}
// Note: initially the size would be deduced from the array literal,
// but it caused source compatibility issues so this is currently not the case.
void tst_QByteArrayView::literals() const
{
const char hello[] = "Hello";
const char hello[] = "Hello\0This shouldn't be found";
QCOMPARE(QByteArrayView(hello).size(), 5);
QCOMPARE(QByteArrayView(hello + 0).size(), 5); // forces decay to pointer
@ -349,6 +352,44 @@ void tst_QByteArrayView::literals() const
QVERIFY(!bv2.isNull());
QVERIFY(!bv2.empty());
QCOMPARE(bv2.size(), 5);
const char abc[] = "abc";
bv = abc;
QCOMPARE(bv.size(), 3);
const char def[3] = {'d', 'e', 'f'};
bv = def;
QCOMPARE(bv.size(), 3);
}
void tst_QByteArrayView::fromArray() const
{
static constexpr char hello[] = "Hello\0abc\0\0.";
constexpr QByteArrayView bv = QByteArrayView::fromArray(hello);
QCOMPARE(bv.size(), 13);
QVERIFY(!bv.empty());
QVERIFY(!bv.isEmpty());
QVERIFY(!bv.isNull());
QCOMPARE(*bv.data(), 'H');
QCOMPARE(bv[0], 'H');
QCOMPARE(bv.at(0), 'H');
QCOMPARE(bv.front(), 'H');
QCOMPARE(bv.first(), 'H');
QCOMPARE(bv[4], 'o');
QCOMPARE(bv.at(4), 'o');
QCOMPARE(bv[5], '\0');
QCOMPARE(bv.at(5), '\0');
QCOMPARE(*(bv.data() + bv.size() - 2), '.');
QCOMPARE(bv.back(), '\0');
QCOMPARE(bv.last(), '\0');
const std::byte bytes[] = {std::byte(0x0), std::byte(0x1), std::byte(0x2)};
QByteArrayView bbv = QByteArrayView::fromArray(bytes);
QCOMPARE(bbv.data(), reinterpret_cast<const char *>(bytes + 0));
QCOMPARE(bbv.size(), 3);
QCOMPARE(bbv.first(), 0x0);
QCOMPARE(bbv.last(), 0x2);
}
void tst_QByteArrayView::literalsWithInternalNulls() const
@ -356,36 +397,39 @@ void tst_QByteArrayView::literalsWithInternalNulls() const
const char withnull[] = "a\0zzz";
// these are different results
QCOMPARE(size_t(QByteArrayView(withnull).size()), sizeof(withnull)/sizeof(withnull[0]) - 1);
QCOMPARE(size_t(QByteArrayView::fromArray(withnull).size()), std::size(withnull));
QCOMPARE(QByteArrayView(withnull + 0).size(), 1);
QByteArrayView nulled(withnull);
QByteArrayView nulled = QByteArrayView::fromArray(withnull);
QCOMPARE(nulled.last(), '\0');
nulled.chop(1); // cut off trailing \0
QCOMPARE(nulled[1], '\0');
QCOMPARE(nulled.indexOf('\0'), 1);
QCOMPARE(nulled.indexOf('z'), 2);
QCOMPARE(nulled.lastIndexOf('z'), 4);
QCOMPARE(nulled.lastIndexOf('a'), 0);
QVERIFY(nulled.startsWith("a\0z"));
QVERIFY(!nulled.startsWith("a\0y"));
QVERIFY(nulled.startsWith("a\0y"));
QVERIFY(!nulled.startsWith(QByteArrayView("a\0y", 3)));
QVERIFY(nulled.endsWith("zz"));
QVERIFY(nulled.contains("z"));
QVERIFY(nulled.contains("\0z"));
QVERIFY(!nulled.contains("\0y"));
QCOMPARE(nulled.first(5), withnull);
QCOMPARE(nulled.last(5), withnull);
QCOMPARE(nulled.sliced(0), withnull);
QVERIFY(nulled.contains(QByteArrayView("\0z", 2)));
QVERIFY(!nulled.contains(QByteArrayView("\0y", 2)));
QCOMPARE(nulled.first(5), QByteArrayView(withnull, 5));
QCOMPARE(nulled.last(5), QByteArrayView(withnull, 5));
QCOMPARE(nulled.sliced(0), QByteArrayView(withnull, 5));
QCOMPARE(nulled.sliced(2, 2), "zz");
QCOMPARE(nulled.chopped(2), "a\0z");
QVERIFY(nulled.chopped(2) != "a\0y");
QCOMPARE(nulled.chopped(2), QByteArrayView("a\0z", 3));
QVERIFY(nulled.chopped(2) != QByteArrayView("a\0y", 3));
QCOMPARE(nulled.count('z'), 3);
const char nullfirst[] = "\0buzz";
QByteArrayView fromnull(nullfirst);
QByteArrayView fromnull = QByteArrayView::fromArray(nullfirst);
QVERIFY(!fromnull.isEmpty());
const char nullNotEnd[] = { 'b', 'o', 'w', '\0', 'a', 'f', 't', 'z' };
QByteArrayView midNull(nullNotEnd);
QCOMPARE(midNull.back(), 't');
QByteArrayView midNull = QByteArrayView::fromArray(nullNotEnd);
QCOMPARE(midNull.back(), 'z');
}
void tst_QByteArrayView::at() const
@ -522,14 +566,14 @@ void tst_QByteArrayView::fromEmptyLiteral() const
QCOMPARE(QByteArrayView(null).size(), 0);
QCOMPARE(QByteArrayView(null).data(), nullptr);
QCOMPARE(QByteArrayView(empty).size(), 0);
QCOMPARE(static_cast<const void*>(QByteArrayView(empty).data()),
QCOMPARE(QByteArrayView::fromArray(empty).size(), 1);
QCOMPARE(static_cast<const void*>(QByteArrayView::fromArray(empty).data()),
static_cast<const void*>(empty));
QVERIFY(QByteArrayView(null).isNull());
QVERIFY(QByteArrayView(null).isEmpty());
QVERIFY(QByteArrayView(empty).isEmpty());
QVERIFY(!QByteArrayView(empty).isNull());
QVERIFY(!QByteArrayView::fromArray(empty).isEmpty());
QVERIFY(!QByteArrayView::fromArray(empty).isNull());
}
template <typename Char>