QStringView: add converting constructor from array-like containers
Centralize, rather than keeping adding constructors from any array-like container. A more robust implementation, likely following the converting constructor for std::span ([span.cons]), is out of scope for C++17 and will require C++20's ranges and concepts. [ChangeLog][QtCore][QStringView] QStringView can now be constructed from any contiguous container, as long as they hold string-like data. For instance, it's now possible to create a QStringView object from a std::vector<char16_t>, a QVarLengthArray<ushort> and so on. Change-Id: I7043eb194f617e98bd1f8af1237777a93a6c5e75 Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
This commit is contained in:
parent
594abde1a2
commit
9e1dc1e8a9
@ -72,6 +72,7 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
class QString;
|
||||
class QStringRef;
|
||||
class QStringView;
|
||||
|
||||
namespace QtPrivate {
|
||||
template <typename Char>
|
||||
@ -85,15 +86,6 @@ template <typename Char>
|
||||
struct IsCompatibleCharType
|
||||
: IsCompatibleCharTypeHelper<typename std::remove_cv<typename std::remove_reference<Char>::type>::type> {};
|
||||
|
||||
template <typename Array>
|
||||
struct IsCompatibleArrayHelper : std::false_type {};
|
||||
template <typename Char, size_t N>
|
||||
struct IsCompatibleArrayHelper<Char[N]>
|
||||
: IsCompatibleCharType<Char> {};
|
||||
template <typename Array>
|
||||
struct IsCompatibleArray
|
||||
: IsCompatibleArrayHelper<typename std::remove_cv<typename std::remove_reference<Array>::type>::type> {};
|
||||
|
||||
template <typename Pointer>
|
||||
struct IsCompatiblePointerHelper : std::false_type {};
|
||||
template <typename Char>
|
||||
@ -103,17 +95,28 @@ template <typename Pointer>
|
||||
struct IsCompatiblePointer
|
||||
: IsCompatiblePointerHelper<typename std::remove_cv<typename std::remove_reference<Pointer>::type>::type> {};
|
||||
|
||||
template <typename T>
|
||||
struct IsCompatibleStdBasicStringHelper : std::false_type {};
|
||||
template <typename Char, typename...Args>
|
||||
struct IsCompatibleStdBasicStringHelper<std::basic_string<Char, Args...> >
|
||||
: IsCompatibleCharType<Char> {};
|
||||
template <typename T, typename Enable = void>
|
||||
struct IsContainerCompatibleWithQStringView : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsCompatibleStdBasicString
|
||||
: IsCompatibleStdBasicStringHelper<
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type
|
||||
> {};
|
||||
struct IsContainerCompatibleWithQStringView<T, std::enable_if_t<std::conjunction_v<
|
||||
// lacking concepts and ranges, we accept any T whose std::data yields a suitable pointer ...
|
||||
IsCompatiblePointer<decltype( std::data(std::declval<const T &>()) )>,
|
||||
// ... and that has a suitable size ...
|
||||
std::is_convertible<decltype( std::size(std::declval<const T &>()) ), qsizetype>,
|
||||
// ... and it's a range as it defines an iterator-like API
|
||||
IsCompatibleCharType<typename std::iterator_traits<decltype( std::begin(std::declval<const T &>()) )>::value_type>,
|
||||
std::is_convertible<
|
||||
decltype( std::begin(std::declval<const T &>()) != std::end(std::declval<const T &>()) ),
|
||||
bool>,
|
||||
|
||||
// These need to be treated specially due to the empty vs null distinction
|
||||
std::negation<std::is_same<std::decay_t<T>, QString>>,
|
||||
std::negation<std::is_same<std::decay_t<T>, QStringRef>>,
|
||||
|
||||
// Don't make an accidental copy constructor
|
||||
std::negation<std::is_same<std::decay_t<T>, QStringView>>
|
||||
>>> : std::true_type {};
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
||||
@ -138,23 +141,14 @@ private:
|
||||
template <typename Char>
|
||||
using if_compatible_char = typename std::enable_if<QtPrivate::IsCompatibleCharType<Char>::value, bool>::type;
|
||||
|
||||
template <typename Array>
|
||||
using if_compatible_array = typename std::enable_if<QtPrivate::IsCompatibleArray<Array>::value, bool>::type;
|
||||
|
||||
template <typename Pointer>
|
||||
using if_compatible_pointer = typename std::enable_if<QtPrivate::IsCompatiblePointer<Pointer>::value, bool>::type;
|
||||
|
||||
template <typename T>
|
||||
using if_compatible_string = typename std::enable_if<QtPrivate::IsCompatibleStdBasicString<T>::value, bool>::type;
|
||||
|
||||
template <typename T>
|
||||
using if_compatible_qstring_like = typename std::enable_if<std::is_same<T, QString>::value || std::is_same<T, QStringRef>::value, bool>::type;
|
||||
|
||||
template <typename Char, size_t N>
|
||||
static Q_DECL_CONSTEXPR qsizetype lengthHelperArray(const Char (&)[N]) noexcept
|
||||
{
|
||||
return qsizetype(N - 1);
|
||||
}
|
||||
template <typename T>
|
||||
using if_compatible_container = typename std::enable_if<QtPrivate::IsContainerCompatibleWithQStringView<T>::value, bool>::type;
|
||||
|
||||
template <typename Char>
|
||||
static qsizetype lengthHelperPointer(const Char *str) noexcept
|
||||
@ -174,6 +168,18 @@ private:
|
||||
return QtPrivate::qustrlen(reinterpret_cast<const char16_t *>(str));
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
static Q_DECL_CONSTEXPR qsizetype lengthHelperContainer(const Container &c) noexcept
|
||||
{
|
||||
return qsizetype(std::size(c));
|
||||
}
|
||||
|
||||
template <typename Char, size_t N>
|
||||
static Q_DECL_CONSTEXPR qsizetype lengthHelperContainer(const Char (&)[N]) noexcept
|
||||
{
|
||||
return qsizetype(N - 1);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
static const storage_type *castHelper(const Char *str) noexcept
|
||||
{ return reinterpret_cast<const storage_type*>(str); }
|
||||
@ -202,9 +208,6 @@ public:
|
||||
template <typename Char>
|
||||
Q_DECL_CONSTEXPR QStringView(const Char *str) noexcept;
|
||||
#else
|
||||
template <typename Array, if_compatible_array<Array> = true>
|
||||
Q_DECL_CONSTEXPR QStringView(const Array &str) noexcept
|
||||
: QStringView(str, lengthHelperArray(str)) {}
|
||||
|
||||
template <typename Pointer, if_compatible_pointer<Pointer> = true>
|
||||
Q_DECL_CONSTEXPR QStringView(const Pointer &str) noexcept
|
||||
@ -220,9 +223,9 @@ public:
|
||||
: QStringView(str.isNull() ? nullptr : str.data(), qsizetype(str.size())) {}
|
||||
#endif
|
||||
|
||||
template <typename StdBasicString, if_compatible_string<StdBasicString> = true>
|
||||
Q_DECL_CONSTEXPR QStringView(const StdBasicString &str) noexcept
|
||||
: QStringView(str.data(), qsizetype(str.size())) {}
|
||||
template <typename Container, if_compatible_container<Container> = true>
|
||||
Q_DECL_CONSTEXPR QStringView(const Container &c) noexcept
|
||||
: QStringView(std::data(c), lengthHelperContainer(c)) {}
|
||||
|
||||
Q_REQUIRED_RESULT inline QString toString() const; // defined in qstring.h
|
||||
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
|
||||
|
@ -30,10 +30,19 @@
|
||||
#include <QString>
|
||||
#include <QChar>
|
||||
#include <QStringRef>
|
||||
#include <QVarLengthArray>
|
||||
#include <QVector>
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
// for negative testing (can't convert from)
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
||||
template <typename T>
|
||||
using CanConvert = std::is_convertible<T, QStringView>;
|
||||
@ -77,29 +86,38 @@ Q_STATIC_ASSERT(CanConvert<ushort[123]>::value);
|
||||
Q_STATIC_ASSERT(CanConvert< ushort*>::value);
|
||||
Q_STATIC_ASSERT(CanConvert<const ushort*>::value);
|
||||
|
||||
static_assert(CanConvert<QVector<ushort>>::value);
|
||||
static_assert(CanConvert<QVarLengthArray<ushort>>::value);
|
||||
static_assert(CanConvert<std::vector<ushort>>::value);
|
||||
static_assert(CanConvert<std::array<ushort, 123>>::value);
|
||||
static_assert(!CanConvert<std::deque<ushort>>::value);
|
||||
static_assert(!CanConvert<std::list<ushort>>::value);
|
||||
|
||||
//
|
||||
// char16_t
|
||||
//
|
||||
|
||||
#if defined(Q_COMPILER_UNICODE_STRINGS)
|
||||
|
||||
Q_STATIC_ASSERT(!CanConvert<char16_t>::value);
|
||||
|
||||
Q_STATIC_ASSERT(CanConvert< char16_t*>::value);
|
||||
Q_STATIC_ASSERT(CanConvert<const char16_t*>::value);
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(Q_STDLIB_UNICODE_STRINGS)
|
||||
|
||||
Q_STATIC_ASSERT(CanConvert< std::u16string >::value);
|
||||
Q_STATIC_ASSERT(CanConvert<const std::u16string >::value);
|
||||
Q_STATIC_ASSERT(CanConvert< std::u16string&>::value);
|
||||
Q_STATIC_ASSERT(CanConvert<const std::u16string&>::value);
|
||||
|
||||
#endif
|
||||
static_assert(CanConvert< std::u16string_view >::value);
|
||||
static_assert(CanConvert<const std::u16string_view >::value);
|
||||
static_assert(CanConvert< std::u16string_view&>::value);
|
||||
static_assert(CanConvert<const std::u16string_view&>::value);
|
||||
|
||||
static_assert(CanConvert<QVector<char16_t>>::value);
|
||||
static_assert(CanConvert<QVarLengthArray<char16_t>>::value);
|
||||
static_assert(CanConvert<std::vector<char16_t>>::value);
|
||||
static_assert(CanConvert<std::array<char16_t, 123>>::value);
|
||||
static_assert(!CanConvert<std::deque<char16_t>>::value);
|
||||
static_assert(!CanConvert<std::list<char16_t>>::value);
|
||||
|
||||
//
|
||||
// wchar_t
|
||||
@ -123,6 +141,17 @@ Q_STATIC_ASSERT(CanConvert<const std::wstring >::value == CanConvertFromWCharT);
|
||||
Q_STATIC_ASSERT(CanConvert< std::wstring&>::value == CanConvertFromWCharT);
|
||||
Q_STATIC_ASSERT(CanConvert<const std::wstring&>::value == CanConvertFromWCharT);
|
||||
|
||||
static_assert(CanConvert< std::wstring_view >::value == CanConvertFromWCharT);
|
||||
static_assert(CanConvert<const std::wstring_view >::value == CanConvertFromWCharT);
|
||||
static_assert(CanConvert< std::wstring_view&>::value == CanConvertFromWCharT);
|
||||
static_assert(CanConvert<const std::wstring_view&>::value == CanConvertFromWCharT);
|
||||
|
||||
static_assert(CanConvert<QVector<wchar_t>>::value == CanConvertFromWCharT);
|
||||
static_assert(CanConvert<QVarLengthArray<wchar_t>>::value == CanConvertFromWCharT);
|
||||
static_assert(CanConvert<std::vector<wchar_t>>::value == CanConvertFromWCharT);
|
||||
static_assert(CanConvert<std::array<wchar_t, 123>>::value == CanConvertFromWCharT);
|
||||
static_assert(!CanConvert<std::deque<wchar_t>>::value);
|
||||
static_assert(!CanConvert<std::list<wchar_t>>::value);
|
||||
|
||||
class tst_QStringView : public QObject
|
||||
{
|
||||
@ -207,6 +236,30 @@ private Q_SLOTS:
|
||||
fromStdString<char16_t>();
|
||||
}
|
||||
|
||||
void fromUShortContainers() const
|
||||
{
|
||||
fromContainers<ushort>();
|
||||
}
|
||||
|
||||
void fromQCharContainers() const
|
||||
{
|
||||
fromContainers<QChar>();
|
||||
}
|
||||
|
||||
void fromChar16TContainers() const
|
||||
{
|
||||
fromContainers<char16_t>();
|
||||
}
|
||||
|
||||
void fromWCharTContainers() const
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
fromContainers<wchar_t>();
|
||||
#else
|
||||
QSKIP("This is a Windows-only test");
|
||||
#endif
|
||||
}
|
||||
|
||||
void comparison();
|
||||
|
||||
void overloadResolution();
|
||||
@ -221,6 +274,8 @@ private:
|
||||
template <typename Char, typename Container>
|
||||
void fromContainer() const;
|
||||
template <typename Char>
|
||||
void fromContainers() const;
|
||||
template <typename Char>
|
||||
void fromStdString() const { fromContainer<Char, std::basic_string<Char> >(); }
|
||||
};
|
||||
|
||||
@ -501,6 +556,14 @@ void tst_QStringView::fromContainer() const
|
||||
conversion_tests(std::move(c));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void tst_QStringView::fromContainers() const
|
||||
{
|
||||
fromContainer<Char, QVector<Char>>();
|
||||
fromContainer<Char, QVarLengthArray<Char>>();
|
||||
fromContainer<Char, std::vector<Char>>();
|
||||
}
|
||||
|
||||
namespace help {
|
||||
template <typename T>
|
||||
size_t size(const T &t) { return size_t(t.size()); }
|
||||
|
Loading…
x
Reference in New Issue
Block a user