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 <qt_ci_bot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
parent
d105a7bd62
commit
f11bc38850
@ -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 <tuple>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
class tst_QOffsetStringArray;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QtPrivate {
|
||||
template<int N, int O, int I, int ... Idx>
|
||||
struct OffsetSequenceHelper : OffsetSequenceHelper<N - 1, O + I, Idx..., O> { };
|
||||
|
||||
template<int Last, int I, int S, int ... Idx>
|
||||
struct OffsetSequenceHelper<1, Last, I, S, Idx...> : IndexesList<Last + I, Idx..., Last>
|
||||
{
|
||||
// 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<quint8>::max(),
|
||||
quint8,
|
||||
typename std::conditional<
|
||||
Last <= std::numeric_limits<quint16>::max(),
|
||||
quint16,
|
||||
int>::type
|
||||
>::type;
|
||||
};
|
||||
|
||||
template<int ... Idx>
|
||||
struct OffsetSequence : OffsetSequenceHelper<sizeof ... (Idx), 0, Idx..., 0> { };
|
||||
|
||||
template<int N>
|
||||
struct StaticString
|
||||
{
|
||||
const char data[N];
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct StaticString<0>
|
||||
{
|
||||
static constexpr int size() noexcept
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename, typename>
|
||||
struct StaticStringBuilder;
|
||||
|
||||
template<int ... I1, int ... I2>
|
||||
struct StaticStringBuilder<IndexesList<I1...>, IndexesList<I2...>>
|
||||
{
|
||||
|
||||
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<int N1, int N2>
|
||||
static constexpr StaticString<N1 + N2> concatenate(
|
||||
const char (&ls)[N1], const StaticString<N2> &rs) noexcept
|
||||
{
|
||||
return StaticString<N1 + N2>{{ls[I1]..., rs.data[I2]...}};
|
||||
}
|
||||
QT_WARNING_POP
|
||||
};
|
||||
|
||||
template<int Sum>
|
||||
constexpr StaticString<0> staticString() noexcept
|
||||
{
|
||||
return StaticString<0>{};
|
||||
}
|
||||
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_MSVC(4503)
|
||||
template<int Sum, int I, int ... Ix>
|
||||
constexpr StaticString<Sum> staticString(const char (&s)[I], const char (&...sx)[Ix]) noexcept
|
||||
{
|
||||
return StaticStringBuilder<
|
||||
makeIndexSequence<I>,
|
||||
makeIndexSequence<Sum - I>>::concatenate(s, staticString<Sum - I>(sx...));
|
||||
}
|
||||
QT_WARNING_POP
|
||||
} // namespace QtPrivate
|
||||
|
||||
QT_WARNING_PUSH
|
||||
#if defined(Q_CC_GNU) && __GNUC__ == 9
|
||||
QT_WARNING_DISABLE_GCC("-Wstringop-overflow")
|
||||
#endif
|
||||
template<typename T, int SizeString, int SizeOffsets>
|
||||
template <typename StaticString, typename OffsetList>
|
||||
class QOffsetStringArray
|
||||
{
|
||||
public:
|
||||
using Type = T;
|
||||
constexpr QOffsetStringArray(const StaticString &string, const OffsetList &offsets)
|
||||
: m_string(string), m_offsets(offsets)
|
||||
{}
|
||||
|
||||
template<int ... Ox>
|
||||
constexpr QOffsetStringArray(const QtPrivate::StaticString<SizeString> &str,
|
||||
QtPrivate::IndexesList<SizeString, Ox...>) 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<SizeString> m_string;
|
||||
const T m_offsets[SizeOffsets];
|
||||
StaticString m_string;
|
||||
OffsetList m_offsets;
|
||||
friend tst_QOffsetStringArray;
|
||||
};
|
||||
QT_WARNING_POP
|
||||
|
||||
template<typename T, int N, int ... Ox>
|
||||
constexpr QOffsetStringArray<T, N, sizeof ... (Ox)> qOffsetStringArray(
|
||||
const QtPrivate::StaticString<N> &string,
|
||||
QtPrivate::IndexesList<N, Ox...> offsets) noexcept
|
||||
namespace QtPrivate {
|
||||
// std::copy is not constexpr in C++17
|
||||
template <typename II, typename OO>
|
||||
static constexpr OO copyData(II input, qsizetype n, OO output)
|
||||
{
|
||||
return QOffsetStringArray<T, N, sizeof ... (Ox)>(
|
||||
string,
|
||||
offsets);
|
||||
using E = decltype(+*output);
|
||||
for (qsizetype i = 0; i < n; ++i)
|
||||
output[i] = E(input[i]);
|
||||
return output + n;
|
||||
}
|
||||
|
||||
template <size_t Highest> constexpr auto minifyValue()
|
||||
{
|
||||
if constexpr (Highest <= std::numeric_limits<quint8>::max()) {
|
||||
return quint8(Highest);
|
||||
} else if constexpr (Highest <= std::numeric_limits<quint16>::max()) {
|
||||
return quint16(Highest);
|
||||
} else {
|
||||
// int is probably enough for everyone
|
||||
return int(Highest);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t StringLength, typename Extractor, typename... T>
|
||||
constexpr auto makeStaticString(Extractor extract, const T &... entries)
|
||||
{
|
||||
std::array<char, StringLength> 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 <size_t N> 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 <size_t KL, size_t VL> struct StaticMapEntry
|
||||
{
|
||||
StaticString<KL> key = {};
|
||||
StaticString<VL> value = {};
|
||||
constexpr StaticMapEntry() = default;
|
||||
constexpr StaticMapEntry(const char (&k)[KL], const char (&v)[VL])
|
||||
: key(k), value(v)
|
||||
{}
|
||||
};
|
||||
|
||||
template <typename StringExtractor, typename... T>
|
||||
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<StringLength>());
|
||||
|
||||
size_t offset = 0;
|
||||
std::array fullOffsetList = { offset += sizeof(extractString(T{}))... };
|
||||
|
||||
// prepend zero, drop last element
|
||||
std::array<MinifiedOffsetType, Count> minifiedOffsetList = {};
|
||||
QtPrivate::copyData(fullOffsetList.begin(), Count - 1, minifiedOffsetList.begin() + 1);
|
||||
|
||||
std::array staticString = QtPrivate::makeStaticString<StringLength>(extractString, entries...);
|
||||
return QOffsetStringArray(staticString, minifiedOffsetList);
|
||||
}
|
||||
}
|
||||
|
||||
template<int ... Nx>
|
||||
struct QOffsetStringArrayRet
|
||||
constexpr auto qOffsetStringArray(const char (&...strings)[Nx]) noexcept
|
||||
{
|
||||
using Offsets = QtPrivate::OffsetSequence<Nx...>;
|
||||
using Type = QOffsetStringArray<typename Offsets::Type, Offsets::Length, sizeof ... (Nx)>;
|
||||
};
|
||||
|
||||
template<int ... Nx>
|
||||
constexpr auto qOffsetStringArray(const char (&...strings)[Nx]) noexcept -> typename QOffsetStringArrayRet<Nx...>::Type
|
||||
{
|
||||
using Offsets = QtPrivate::OffsetSequence<Nx...>;
|
||||
return qOffsetStringArray<typename Offsets::Type>(
|
||||
QtPrivate::staticString<Offsets::Length>(strings...), Offsets{});
|
||||
auto extractString = [](const auto &s) -> decltype(auto) { return s; };
|
||||
return QtPrivate::qOffsetStringArray(extractString, QtPrivate::StaticString(strings)...);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -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()
|
||||
|
@ -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<decltype(messages)::Type, quint8>::value, "messages::Type != quint8");
|
||||
static_assert(messages.m_string.size() == 51);
|
||||
static_assert(messages.m_offsets.size() == 6);
|
||||
static_assert(std::is_same_v<decltype(messages.m_offsets)::value_type, quint8>);
|
||||
|
||||
static_assert(messages257.sizeOffsets == 257, "messages257.sizeOffsets");
|
||||
static_assert(messages257.sizeString == 260, "messages257.sizeString");
|
||||
static_assert(std::is_same<decltype(messages257)::Type, quint16>::value,
|
||||
"messages257::Type != quint16");
|
||||
static_assert(messages257.m_offsets.size() == 257);
|
||||
static_assert(messages257.m_string.size() == 260);
|
||||
static_assert(std::is_same_v<decltype(messages257.m_offsets)::value_type, quint16>);
|
||||
|
||||
static_assert(messagesBigOffsets.sizeOffsets == 4, "messagesBigOffsets.sizeOffsets");
|
||||
static_assert(messagesBigOffsets.sizeString == 364, "messagesBigOffsets.sizeString");
|
||||
static_assert(std::is_same<decltype(messagesBigOffsets)::Type, quint16>::value,
|
||||
"messagesBigOffsets::Type != quint16");
|
||||
static_assert(messagesBigOffsets.m_offsets.size() == 4);
|
||||
static_assert(messagesBigOffsets.m_string.size() == 364);
|
||||
static_assert(std::is_same_v<decltype(messagesBigOffsets.m_offsets)::value_type, quint16>);
|
||||
}
|
||||
|
||||
void tst_QOffsetStringArray::access()
|
||||
|
Loading…
x
Reference in New Issue
Block a user