Introducing Q_ARRAY_LITERAL
This provides the same functionality as the specialized QStringLiteral and QByteArrayLiteral, but on top of QArrayData. The macro has two variations, variadic and simple. The variadic version depends on compiler support for (C99) variadic macros and enables static initialization of arrays of any POD data. Use of this macro is not recommended on code or applications that need to work in configurations where variadic macros are not supported. The simple version is more portable and is enough to support the use cases of QStringLiteral and QByteArrayLiteral, also providing a fallback that allocates and copies data when static initialization is not available. Change-Id: I7154a24dcae4bbbd7d5978653f620138467830c5 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
f218213a42
commit
b59d831980
@ -43,6 +43,7 @@
|
||||
#define QARRAYDATA_H
|
||||
|
||||
#include <QtCore/qrefcount.h>
|
||||
#include <string.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
@ -190,6 +191,84 @@ struct QArrayDataPointerRef
|
||||
& ~(Q_ALIGNOF(type) - 1) } \
|
||||
/**/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Q_ARRAY_LITERAL
|
||||
|
||||
// The idea here is to place a (read-only) copy of header and array data in an
|
||||
// mmappable portion of the executable (typically, .rodata section). This is
|
||||
// accomplished by hiding a static const instance of QStaticArrayData, which is
|
||||
// POD.
|
||||
|
||||
#if defined(Q_COMPILER_VARIADIC_MACROS)
|
||||
#if defined(Q_COMPILER_LAMBDA)
|
||||
// Hide array inside a lambda
|
||||
#define Q_ARRAY_LITERAL(Type, ...) \
|
||||
([]() -> QArrayDataPointerRef<Type> { \
|
||||
/* MSVC 2010 Doesn't support static variables in a lambda, but */ \
|
||||
/* happily accepts them in a static function of a lambda-local */ \
|
||||
/* struct :-) */ \
|
||||
struct StaticWrapper { \
|
||||
static QArrayDataPointerRef<Type> get() \
|
||||
{ \
|
||||
Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \
|
||||
return ref; \
|
||||
} \
|
||||
}; \
|
||||
return StaticWrapper::get(); \
|
||||
}()) \
|
||||
/**/
|
||||
#elif defined(Q_CC_GNU)
|
||||
// Hide array within GCC's __extension__ {( )} block
|
||||
#define Q_ARRAY_LITERAL(Type, ...) \
|
||||
__extension__ ({ \
|
||||
Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \
|
||||
ref; \
|
||||
}) \
|
||||
/**/
|
||||
#endif
|
||||
#endif // defined(Q_COMPILER_VARIADIC_MACROS)
|
||||
|
||||
#if defined(Q_ARRAY_LITERAL)
|
||||
#define Q_ARRAY_LITERAL_IMPL(Type, ...) \
|
||||
union { Type type_must_be_POD; } dummy; Q_UNUSED(dummy) \
|
||||
\
|
||||
/* Portable compile-time array size computation */ \
|
||||
Type data[] = { __VA_ARGS__ }; Q_UNUSED(data) \
|
||||
enum { Size = sizeof(data) / sizeof(data[0]) }; \
|
||||
\
|
||||
static const QStaticArrayData<Type, Size> literal = { \
|
||||
Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(Type, Size), { __VA_ARGS__ } }; \
|
||||
\
|
||||
QArrayDataPointerRef<Type> ref = \
|
||||
{ static_cast<QTypedArrayData<Type> *>( \
|
||||
const_cast<QArrayData *>(&literal.header)) }; \
|
||||
/**/
|
||||
#else
|
||||
// As a fallback, memory is allocated and data copied to the heap.
|
||||
|
||||
// The fallback macro does NOT use variadic macros and does NOT support
|
||||
// variable number of arguments. It is suitable for char arrays.
|
||||
|
||||
namespace QtPrivate {
|
||||
template <class T, size_t N>
|
||||
inline QArrayDataPointerRef<T> qMakeArrayLiteral(const T (&array)[N])
|
||||
{
|
||||
union { T type_must_be_POD; } dummy; Q_UNUSED(dummy)
|
||||
|
||||
QArrayDataPointerRef<T> result = { QTypedArrayData<T>::allocate(N) };
|
||||
Q_CHECK_PTR(result.ptr);
|
||||
|
||||
::memcpy(result.ptr->data(), array, N * sizeof(T));
|
||||
result.ptr->size = N;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
#define Q_ARRAY_LITERAL(Type, Array) \
|
||||
QT_PREPEND_NAMESPACE(QtPrivate::qMakeArrayLiteral)<Type>( Array )
|
||||
#endif // !defined(Q_ARRAY_LITERAL)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
@ -77,6 +77,11 @@ public:
|
||||
d->copyAppend(begin, end);
|
||||
}
|
||||
|
||||
SimpleVector(QArrayDataPointerRef<T> ptr)
|
||||
: d(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
explicit SimpleVector(Data *ptr)
|
||||
: d(ptr)
|
||||
{
|
||||
|
@ -82,6 +82,8 @@ private slots:
|
||||
void setSharable_data();
|
||||
void setSharable();
|
||||
void fromRawData();
|
||||
void literals();
|
||||
void variadicLiterals();
|
||||
};
|
||||
|
||||
template <class T> const T &const_(const T &t) { return t; }
|
||||
@ -1182,5 +1184,95 @@ void tst_QArrayData::fromRawData()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QArrayData::literals()
|
||||
{
|
||||
{
|
||||
QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ");
|
||||
QCOMPARE(d->size, 10 + 1);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
QCOMPARE(d->data()[i], char('A' + i));
|
||||
}
|
||||
|
||||
{
|
||||
// wchar_t is not necessarily 2-bytes
|
||||
QArrayDataPointer<wchar_t> d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ");
|
||||
QCOMPARE(d->size, 10 + 1);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
QCOMPARE(d->data()[i], wchar_t('A' + i));
|
||||
}
|
||||
|
||||
{
|
||||
SimpleVector<char> v = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ");
|
||||
|
||||
QVERIFY(!v.isNull());
|
||||
QVERIFY(!v.isEmpty());
|
||||
QCOMPARE(v.size(), size_t(11));
|
||||
// v.capacity() is unspecified, for now
|
||||
|
||||
#if defined(Q_COMPILER_VARIADIC_MACROS) \
|
||||
&& (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU))
|
||||
QVERIFY(v.isStatic());
|
||||
#endif
|
||||
|
||||
QVERIFY(v.isSharable());
|
||||
QVERIFY(v.constBegin() + v.size() == v.constEnd());
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
QCOMPARE(const_(v)[i], char('A' + i));
|
||||
QCOMPARE(const_(v)[10], char('\0'));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QArrayData::variadicLiterals()
|
||||
{
|
||||
#if defined(Q_COMPILER_VARIADIC_MACROS) \
|
||||
&& (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU))
|
||||
{
|
||||
QArrayDataPointer<int> d =
|
||||
Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
QCOMPARE(d->size, 10);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
QCOMPARE(d->data()[i], i);
|
||||
}
|
||||
|
||||
{
|
||||
QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char,
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
|
||||
QCOMPARE(d->size, 10);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
QCOMPARE(d->data()[i], char('A' + i));
|
||||
}
|
||||
|
||||
{
|
||||
QArrayDataPointer<const char *> d = Q_ARRAY_LITERAL(const char *,
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J");
|
||||
QCOMPARE(d->size, 10);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
QCOMPARE(d->data()[i][0], char('A' + i));
|
||||
QCOMPARE(d->data()[i][1], '\0');
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
SimpleVector<int> v = Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6);
|
||||
|
||||
QVERIFY(!v.isNull());
|
||||
QVERIFY(!v.isEmpty());
|
||||
QCOMPARE(v.size(), size_t(7));
|
||||
// v.capacity() is unspecified, for now
|
||||
|
||||
QVERIFY(v.isStatic());
|
||||
|
||||
QVERIFY(v.isSharable());
|
||||
QVERIFY(v.constBegin() + v.size() == v.constEnd());
|
||||
|
||||
for (int i = 0; i < 7; ++i)
|
||||
QCOMPARE(const_(v)[i], i);
|
||||
}
|
||||
#else
|
||||
QSKIP("Variadic Q_ARRAY_LITERAL not available in current configuration.");
|
||||
#endif // defined(Q_COMPILER_VARIADIC_MACROS)
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QArrayData)
|
||||
#include "tst_qarraydata.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user