-
-
Notifications
You must be signed in to change notification settings - Fork 449
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add string literal suffixes to create
QString
s, QByteArray
s, and …
…`QLatin1String(View)`s at compile time (#4706)
- Loading branch information
Showing
4 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
#pragma once | ||
|
||
#include <QString> | ||
|
||
/// This namespace defines the string suffixes _s, _ba, and _L1 used to create Qt types at compile-time. | ||
/// They're easier to use comapred to their corresponding macros. | ||
/// | ||
/// * u"foobar"_s creates a QString (like QStringLiteral). The u prefix is required. | ||
/// | ||
/// * "foobar"_ba creates a QByteArray (like QByteArrayLiteral). | ||
/// | ||
/// * "foobar"_L1 creates a QLatin1String(-View). | ||
namespace chatterino::literals { | ||
|
||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | ||
|
||
// This makes sure that the backing data never causes allocation after compilation. | ||
// It's essentially the QStringLiteral macro inlined. | ||
// | ||
// From desktop-app/lib_base | ||
// https://github.com/desktop-app/lib_base/blob/f904c60987115a4b514a575b23009ff25de0fafa/base/basic_types.h#L63-L152 | ||
// And qt/qtbase (5.15) | ||
// https://github.com/qt/qtbase/blob/29400a683f96867133b28299c0d0bd6bcf40df35/src/corelib/text/qstringliteral.h#L64-L104 | ||
namespace detail { | ||
// NOLINTBEGIN(modernize-avoid-c-arrays) | ||
// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays) | ||
// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) | ||
|
||
template <size_t N> | ||
struct LiteralResolver { | ||
template <size_t... I> | ||
constexpr LiteralResolver(const char16_t (&text)[N], | ||
std::index_sequence<I...> /*seq*/) | ||
: utf16Text{text[I]...} | ||
{ | ||
} | ||
template <size_t... I> | ||
constexpr LiteralResolver(const char (&text)[N], | ||
std::index_sequence<I...> /*seq*/) | ||
: latin1Text{text[I]...} | ||
, latin1(true) | ||
{ | ||
} | ||
constexpr LiteralResolver(const char16_t (&text)[N]) | ||
: LiteralResolver(text, std::make_index_sequence<N>{}) | ||
{ | ||
} | ||
constexpr LiteralResolver(const char (&text)[N]) | ||
: LiteralResolver(text, std::make_index_sequence<N>{}) | ||
{ | ||
} | ||
|
||
const char16_t utf16Text[N]{}; | ||
const char latin1Text[N]{}; | ||
size_t length = N; | ||
bool latin1 = false; | ||
}; | ||
|
||
template <size_t N> | ||
struct StaticStringData { | ||
template <std::size_t... I> | ||
constexpr StaticStringData(const char16_t (&text)[N], | ||
std::index_sequence<I...> /*seq*/) | ||
: data Q_STATIC_STRING_DATA_HEADER_INITIALIZER(N - 1) | ||
, text{text[I]...} | ||
{ | ||
} | ||
QArrayData data; | ||
char16_t text[N]; | ||
|
||
QStringData *pointer() | ||
{ | ||
Q_ASSERT(data.ref.isStatic()); | ||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) | ||
return static_cast<QStringData *>(&data); | ||
} | ||
}; | ||
|
||
template <size_t N> | ||
struct StaticByteArrayData { | ||
template <std::size_t... I> | ||
constexpr StaticByteArrayData(const char (&text)[N], | ||
std::index_sequence<I...> /*seq*/) | ||
: data Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER(N - 1) | ||
, text{text[I]...} | ||
{ | ||
} | ||
QByteArrayData data; | ||
char text[N]; | ||
|
||
QByteArrayData *pointer() | ||
{ | ||
Q_ASSERT(data.ref.isStatic()); | ||
return &data; | ||
} | ||
}; | ||
|
||
// NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members) | ||
// NOLINTEND(cppcoreguidelines-avoid-c-arrays) | ||
// NOLINTEND(modernize-avoid-c-arrays) | ||
|
||
} // namespace detail | ||
|
||
template <detail::LiteralResolver R> | ||
inline QString operator""_s() noexcept | ||
{ | ||
static_assert(R.length > 0); // always has a terminating null | ||
static_assert(!R.latin1, "QString literals must be made up of 16bit " | ||
"characters. Forgot a u\"\"?"); | ||
|
||
static auto literal = detail::StaticStringData<R.length>( | ||
R.utf16Text, std::make_index_sequence<R.length>{}); | ||
return QString{QStringDataPtr{literal.pointer()}}; | ||
}; | ||
|
||
template <detail::LiteralResolver R> | ||
inline QByteArray operator""_ba() noexcept | ||
{ | ||
static_assert(R.length > 0); // always has a terminating null | ||
static_assert(R.latin1, "QByteArray literals must be made up of 8bit " | ||
"characters. Misplaced u\"\"?"); | ||
|
||
static auto literal = detail::StaticByteArrayData<R.length>( | ||
R.latin1Text, std::make_index_sequence<R.length>{}); | ||
return QByteArray{QByteArrayDataPtr{literal.pointer()}}; | ||
}; | ||
|
||
#elif QT_VERSION < QT_VERSION_CHECK(6, 4, 0) | ||
|
||
// The operators were added in 6.4, but their implementation works in any 6.x version. | ||
// | ||
// NOLINTBEGIN(cppcoreguidelines-pro-type-const-cast) | ||
inline QString operator""_s(const char16_t *str, size_t size) noexcept | ||
{ | ||
return QString( | ||
QStringPrivate(nullptr, const_cast<char16_t *>(str), qsizetype(size))); | ||
} | ||
|
||
inline QByteArray operator""_ba(const char *str, size_t size) noexcept | ||
{ | ||
return QByteArray( | ||
QByteArrayData(nullptr, const_cast<char *>(str), qsizetype(size))); | ||
} | ||
// NOLINTEND(cppcoreguidelines-pro-type-const-cast) | ||
|
||
#else | ||
|
||
inline QString operator""_s(const char16_t *str, size_t size) noexcept | ||
{ | ||
return Qt::Literals::StringLiterals::operator""_s(str, size); | ||
} | ||
|
||
inline QByteArray operator""_ba(const char *str, size_t size) noexcept | ||
{ | ||
return Qt::Literals::StringLiterals::operator""_ba(str, size); | ||
} | ||
|
||
#endif | ||
|
||
constexpr inline QLatin1String operator""_L1(const char *str, | ||
size_t size) noexcept | ||
{ | ||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | ||
using SizeType = int; | ||
#else | ||
using SizeType = qsizetype; | ||
#endif | ||
|
||
return QLatin1String{str, static_cast<SizeType>(size)}; | ||
} | ||
|
||
} // namespace chatterino::literals |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#include "common/Literals.hpp" | ||
|
||
#include <gtest/gtest.h> | ||
|
||
using namespace chatterino::literals; | ||
|
||
// These tests ensure that the behavior of the suffixes is the same across Qt versions. | ||
|
||
TEST(Literals, String) | ||
{ | ||
ASSERT_EQ(u""_s, QStringLiteral("")); | ||
ASSERT_EQ(u"1"_s, QStringLiteral("1")); | ||
ASSERT_EQ(u"12"_s, QStringLiteral("12")); | ||
ASSERT_EQ(u"123"_s, QStringLiteral("123")); | ||
} | ||
|
||
TEST(Literals, Latin1String) | ||
{ | ||
ASSERT_EQ(""_L1, QLatin1String("")); | ||
ASSERT_EQ("1"_L1, QLatin1String("1")); | ||
ASSERT_EQ("12"_L1, QLatin1String("12")); | ||
ASSERT_EQ("123"_L1, QLatin1String("123")); | ||
|
||
ASSERT_EQ(""_L1, u""_s); | ||
ASSERT_EQ("1"_L1, u"1"_s); | ||
ASSERT_EQ("12"_L1, u"12"_s); | ||
ASSERT_EQ("123"_L1, u"123"_s); | ||
|
||
ASSERT_EQ(QString(""_L1), u""_s); | ||
ASSERT_EQ(QString("1"_L1), u"1"_s); | ||
ASSERT_EQ(QString("12"_L1), u"12"_s); | ||
ASSERT_EQ(QString("123"_L1), u"123"_s); | ||
} | ||
|
||
TEST(Literals, ByteArray) | ||
{ | ||
ASSERT_EQ(""_ba, QByteArrayLiteral("")); | ||
ASSERT_EQ("1"_ba, QByteArrayLiteral("1")); | ||
ASSERT_EQ("12"_ba, QByteArrayLiteral("12")); | ||
ASSERT_EQ("123"_ba, QByteArrayLiteral("123")); | ||
} |