From f11bc388508359b070320866eab4c917cb4c4739 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 20 Sep 2021 21:38:40 -0700 Subject: [PATCH] QOffsetStringArray: rewrite in modern C++17 Less clunky due to having better constexpr support, plus fold expressions. Change-Id: I3eb1bd30e0124f89a052fffd16a6bc73ba79ec19 Reviewed-by: Qt CI Bot Reviewed-by: Lars Knoll --- src/corelib/tools/qoffsetstringarray_p.h | 215 ++++++++---------- .../tools/qoffsetstringarray/CMakeLists.txt | 7 + .../tst_qoffsetstringarray.cpp | 20 +- 3 files changed, 109 insertions(+), 133 deletions(-) diff --git a/src/corelib/tools/qoffsetstringarray_p.h b/src/corelib/tools/qoffsetstringarray_p.h index a5d54546eaf..6e0cb1f30bd 100644 --- a/src/corelib/tools/qoffsetstringarray_p.h +++ b/src/corelib/tools/qoffsetstringarray_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2021 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -53,152 +54,122 @@ #include "private/qglobal_p.h" -#include #include #include +#include +#include + +class tst_QOffsetStringArray; QT_BEGIN_NAMESPACE -namespace QtPrivate { -template -struct OffsetSequenceHelper : OffsetSequenceHelper { }; -template -struct OffsetSequenceHelper<1, Last, I, S, Idx...> : IndexesList -{ - // the unary + before std::numeric_limits below is required. Otherwise we get on g++-10.2: - // error: comparison is always false due to limited range of data type [-Werror=type-limits] - static const constexpr auto Length = Last + I; - using Type = typename std::conditional< - Last <= +std::numeric_limits::max(), - quint8, - typename std::conditional< - Last <= std::numeric_limits::max(), - quint16, - int>::type - >::type; -}; - -template -struct OffsetSequence : OffsetSequenceHelper { }; - -template -struct StaticString -{ - const char data[N]; -}; - - -template<> -struct StaticString<0> -{ - static constexpr int size() noexcept - { - return 0; - } -}; - -template -struct StaticStringBuilder; - -template -struct StaticStringBuilder, IndexesList> -{ - -QT_WARNING_PUSH -QT_WARNING_DISABLE_MSVC(4100) // The formal parameter is not referenced in the body of the function. - // The unreferenced parameter is ignored. - // It happens when 'rs' is StaticString<0> - template - static constexpr StaticString concatenate( - const char (&ls)[N1], const StaticString &rs) noexcept - { - return StaticString{{ls[I1]..., rs.data[I2]...}}; - } -QT_WARNING_POP -}; - -template -constexpr StaticString<0> staticString() noexcept -{ - return StaticString<0>{}; -} - -QT_WARNING_PUSH -QT_WARNING_DISABLE_MSVC(4503) -template -constexpr StaticString staticString(const char (&s)[I], const char (&...sx)[Ix]) noexcept -{ - return StaticStringBuilder< - makeIndexSequence, - makeIndexSequence>::concatenate(s, staticString(sx...)); -} -QT_WARNING_POP -} // namespace QtPrivate - -QT_WARNING_PUSH -#if defined(Q_CC_GNU) && __GNUC__ == 9 -QT_WARNING_DISABLE_GCC("-Wstringop-overflow") -#endif -template +template class QOffsetStringArray { public: - using Type = T; + constexpr QOffsetStringArray(const StaticString &string, const OffsetList &offsets) + : m_string(string), m_offsets(offsets) + {} - template - constexpr QOffsetStringArray(const QtPrivate::StaticString &str, - QtPrivate::IndexesList) noexcept - : m_string(str), - m_offsets{Ox...} - { } - - constexpr inline const char *operator[](const int index) const noexcept + constexpr const char *operator[](const int index) const noexcept { - return m_string.data + m_offsets[qBound(int(0), index, SizeOffsets - 1)]; + return m_string.data() + m_offsets[qBound(int(0), index, count() - 1)]; } - constexpr inline const char *at(const int index) const noexcept + constexpr const char *at(const int index) const noexcept { - return m_string.data + m_offsets[index]; + return m_string.data() + m_offsets[index]; } - constexpr inline const char *str() const { return m_string.data; } - constexpr inline const T *offsets() const { return m_offsets; } - constexpr inline int count() const { return SizeOffsets; } - - static constexpr const auto sizeString = SizeString; - static constexpr const auto sizeOffsets = SizeOffsets; + constexpr int count() const { return int(m_offsets.size()); } private: - QtPrivate::StaticString m_string; - const T m_offsets[SizeOffsets]; + StaticString m_string; + OffsetList m_offsets; + friend tst_QOffsetStringArray; }; -QT_WARNING_POP -template -constexpr QOffsetStringArray qOffsetStringArray( - const QtPrivate::StaticString &string, - QtPrivate::IndexesList offsets) noexcept +namespace QtPrivate { +// std::copy is not constexpr in C++17 +template +static constexpr OO copyData(II input, qsizetype n, OO output) { - return QOffsetStringArray( - string, - offsets); + using E = decltype(+*output); + for (qsizetype i = 0; i < n; ++i) + output[i] = E(input[i]); + return output + n; +} + +template constexpr auto minifyValue() +{ + if constexpr (Highest <= std::numeric_limits::max()) { + return quint8(Highest); + } else if constexpr (Highest <= std::numeric_limits::max()) { + return quint16(Highest); + } else { + // int is probably enough for everyone + return int(Highest); + } +} + +template +constexpr auto makeStaticString(Extractor extract, const T &... entries) +{ + std::array result = {}; + qptrdiff offset = 0; + + const char *strings[] = { extract(entries).operator const char *()... }; + size_t lengths[] = { sizeof(extract(T{}))... }; + for (size_t i = 0; i < std::size(strings); ++i) { + copyData(strings[i], lengths[i], result.begin() + offset); + offset += lengths[i]; + } + return result; +} + +template struct StaticString +{ + char value[N] = {}; + constexpr StaticString() = default; + constexpr StaticString(const char (&s)[N]) { copyData(s, N, value); } + constexpr operator const char *() const { return value; } +}; + +template struct StaticMapEntry +{ + StaticString key = {}; + StaticString value = {}; + constexpr StaticMapEntry() = default; + constexpr StaticMapEntry(const char (&k)[KL], const char (&v)[VL]) + : key(k), value(v) + {} +}; + +template +constexpr auto qOffsetStringArray(StringExtractor extractString, const T &... entries) +{ + constexpr size_t Count = sizeof...(T); + constexpr qsizetype StringLength = (sizeof(extractString(T{})) + ...); + using MinifiedOffsetType = decltype(QtPrivate::minifyValue()); + + size_t offset = 0; + std::array fullOffsetList = { offset += sizeof(extractString(T{}))... }; + + // prepend zero, drop last element + std::array minifiedOffsetList = {}; + QtPrivate::copyData(fullOffsetList.begin(), Count - 1, minifiedOffsetList.begin() + 1); + + std::array staticString = QtPrivate::makeStaticString(extractString, entries...); + return QOffsetStringArray(staticString, minifiedOffsetList); +} } template -struct QOffsetStringArrayRet +constexpr auto qOffsetStringArray(const char (&...strings)[Nx]) noexcept { - using Offsets = QtPrivate::OffsetSequence; - using Type = QOffsetStringArray; -}; - -template -constexpr auto qOffsetStringArray(const char (&...strings)[Nx]) noexcept -> typename QOffsetStringArrayRet::Type -{ - using Offsets = QtPrivate::OffsetSequence; - return qOffsetStringArray( - QtPrivate::staticString(strings...), Offsets{}); + auto extractString = [](const auto &s) -> decltype(auto) { return s; }; + return QtPrivate::qOffsetStringArray(extractString, QtPrivate::StaticString(strings)...); } QT_END_NAMESPACE diff --git a/tests/auto/corelib/tools/qoffsetstringarray/CMakeLists.txt b/tests/auto/corelib/tools/qoffsetstringarray/CMakeLists.txt index 7584d580ecb..67dffe02845 100644 --- a/tests/auto/corelib/tools/qoffsetstringarray/CMakeLists.txt +++ b/tests/auto/corelib/tools/qoffsetstringarray/CMakeLists.txt @@ -10,3 +10,10 @@ qt_internal_add_test(tst_qoffsetstringarray PUBLIC_LIBRARIES Qt::CorePrivate ) + +if (CLANG) + target_compile_options(tst_qoffsetstringarray + PUBLIC -fbracket-depth=512) +elseif (GCC) + # fconstexpr-depth= defaults to 512 +endif() diff --git a/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp b/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp index 9445366efca..0130939b4ef 100644 --- a/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp +++ b/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp @@ -90,19 +90,17 @@ constexpr const auto messagesBigOffsets = qOffsetStringArray( void tst_QOffsetStringArray::init() { - static_assert(messages.sizeString == 51, "message.sizeString"); - static_assert(messages.sizeOffsets == 6, "message.sizeOffsets"); - static_assert(std::is_same::value, "messages::Type != quint8"); + static_assert(messages.m_string.size() == 51); + static_assert(messages.m_offsets.size() == 6); + static_assert(std::is_same_v); - static_assert(messages257.sizeOffsets == 257, "messages257.sizeOffsets"); - static_assert(messages257.sizeString == 260, "messages257.sizeString"); - static_assert(std::is_same::value, - "messages257::Type != quint16"); + static_assert(messages257.m_offsets.size() == 257); + static_assert(messages257.m_string.size() == 260); + static_assert(std::is_same_v); - static_assert(messagesBigOffsets.sizeOffsets == 4, "messagesBigOffsets.sizeOffsets"); - static_assert(messagesBigOffsets.sizeString == 364, "messagesBigOffsets.sizeString"); - static_assert(std::is_same::value, - "messagesBigOffsets::Type != quint16"); + static_assert(messagesBigOffsets.m_offsets.size() == 4); + static_assert(messagesBigOffsets.m_string.size() == 364); + static_assert(std::is_same_v); } void tst_QOffsetStringArray::access()