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/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the QtCore module of the Qt Toolkit.
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
@ -53,152 +54,122 @@
|
|||||||
|
|
||||||
#include "private/qglobal_p.h"
|
#include "private/qglobal_p.h"
|
||||||
|
|
||||||
#include <tuple>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
class tst_QOffsetStringArray;
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
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>
|
template <typename StaticString, typename OffsetList>
|
||||||
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>
|
|
||||||
class QOffsetStringArray
|
class QOffsetStringArray
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Type = T;
|
constexpr QOffsetStringArray(const StaticString &string, const OffsetList &offsets)
|
||||||
|
: m_string(string), m_offsets(offsets)
|
||||||
|
{}
|
||||||
|
|
||||||
template<int ... Ox>
|
constexpr const char *operator[](const int index) const noexcept
|
||||||
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
|
|
||||||
{
|
{
|
||||||
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 int count() const { return int(m_offsets.size()); }
|
||||||
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;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QtPrivate::StaticString<SizeString> m_string;
|
StaticString m_string;
|
||||||
const T m_offsets[SizeOffsets];
|
OffsetList m_offsets;
|
||||||
|
friend tst_QOffsetStringArray;
|
||||||
};
|
};
|
||||||
QT_WARNING_POP
|
|
||||||
|
|
||||||
template<typename T, int N, int ... Ox>
|
namespace QtPrivate {
|
||||||
constexpr QOffsetStringArray<T, N, sizeof ... (Ox)> qOffsetStringArray(
|
// std::copy is not constexpr in C++17
|
||||||
const QtPrivate::StaticString<N> &string,
|
template <typename II, typename OO>
|
||||||
QtPrivate::IndexesList<N, Ox...> offsets) noexcept
|
static constexpr OO copyData(II input, qsizetype n, OO output)
|
||||||
{
|
{
|
||||||
return QOffsetStringArray<T, N, sizeof ... (Ox)>(
|
using E = decltype(+*output);
|
||||||
string,
|
for (qsizetype i = 0; i < n; ++i)
|
||||||
offsets);
|
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>
|
template<int ... Nx>
|
||||||
struct QOffsetStringArrayRet
|
constexpr auto qOffsetStringArray(const char (&...strings)[Nx]) noexcept
|
||||||
{
|
{
|
||||||
using Offsets = QtPrivate::OffsetSequence<Nx...>;
|
auto extractString = [](const auto &s) -> decltype(auto) { return s; };
|
||||||
using Type = QOffsetStringArray<typename Offsets::Type, Offsets::Length, sizeof ... (Nx)>;
|
return QtPrivate::qOffsetStringArray(extractString, QtPrivate::StaticString(strings)...);
|
||||||
};
|
|
||||||
|
|
||||||
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{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -10,3 +10,10 @@ qt_internal_add_test(tst_qoffsetstringarray
|
|||||||
PUBLIC_LIBRARIES
|
PUBLIC_LIBRARIES
|
||||||
Qt::CorePrivate
|
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()
|
void tst_QOffsetStringArray::init()
|
||||||
{
|
{
|
||||||
static_assert(messages.sizeString == 51, "message.sizeString");
|
static_assert(messages.m_string.size() == 51);
|
||||||
static_assert(messages.sizeOffsets == 6, "message.sizeOffsets");
|
static_assert(messages.m_offsets.size() == 6);
|
||||||
static_assert(std::is_same<decltype(messages)::Type, quint8>::value, "messages::Type != quint8");
|
static_assert(std::is_same_v<decltype(messages.m_offsets)::value_type, quint8>);
|
||||||
|
|
||||||
static_assert(messages257.sizeOffsets == 257, "messages257.sizeOffsets");
|
static_assert(messages257.m_offsets.size() == 257);
|
||||||
static_assert(messages257.sizeString == 260, "messages257.sizeString");
|
static_assert(messages257.m_string.size() == 260);
|
||||||
static_assert(std::is_same<decltype(messages257)::Type, quint16>::value,
|
static_assert(std::is_same_v<decltype(messages257.m_offsets)::value_type, quint16>);
|
||||||
"messages257::Type != quint16");
|
|
||||||
|
|
||||||
static_assert(messagesBigOffsets.sizeOffsets == 4, "messagesBigOffsets.sizeOffsets");
|
static_assert(messagesBigOffsets.m_offsets.size() == 4);
|
||||||
static_assert(messagesBigOffsets.sizeString == 364, "messagesBigOffsets.sizeString");
|
static_assert(messagesBigOffsets.m_string.size() == 364);
|
||||||
static_assert(std::is_same<decltype(messagesBigOffsets)::Type, quint16>::value,
|
static_assert(std::is_same_v<decltype(messagesBigOffsets.m_offsets)::value_type, quint16>);
|
||||||
"messagesBigOffsets::Type != quint16");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QOffsetStringArray::access()
|
void tst_QOffsetStringArray::access()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user