QChar: add fromUcs{2,4}()
The fromUcs2() named ctor is designed to replace all the non-char integral-type constructors of QChar which make it very hard to control the implicit QChar conversions, which have caused a few bugs in Qt itself. As a classical named contructor, it simply returns QChar. The fromUcs4() named "ctor", however, needs to expand surrogate pairs, and thus can't just return QChar. Instead, it returns a small struct that contains one or two char16_t's, can be iterated over and be implicitly converted to QStringView. To avoid bikeshedding the name (FromUcs4Result, of course :), it's defined inline and thus can't be named outside the function. This function replaces most uses of QChar::requiresSurrogates() in QtBase. [ChangeLog][QtCore][QChar] Added fromUcs2(), fromUcs4(). Change-Id: I803708c14001040f75cb599e33c24a3fb8d2579c Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
19e7c0d2b5
commit
928d57d8da
@ -710,6 +710,46 @@ QT_BEGIN_NAMESPACE
|
||||
Constructs a QChar for the character with Unicode code point \a code.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn static QChar QChar::fromUcs2(char16_t c)
|
||||
\since 6.0
|
||||
|
||||
Constructs a QChar from UTF-16 character \a c.
|
||||
|
||||
\sa fromUcs4()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn static auto QChar::fromUcs4(char32_t c)
|
||||
\since 6.0
|
||||
|
||||
Returns an anonymous struct that
|
||||
\list
|
||||
\li contains a \c{char16_t chars[2]} array,
|
||||
\li can be implicitly converted to a QStringView, and
|
||||
\li iterated over with a C++11 ranged for loop.
|
||||
\endlist
|
||||
|
||||
If \a c requires surrogates, \c{chars[0]} contains the high surrogate
|
||||
and \c{chars[1]} the low surrogate, and the QStringView has size 2.
|
||||
Otherwise, \c{chars[0]} contains \a c and \c{chars[1]} is
|
||||
\l{QChar::isNull}{null}, and the QStringView has size 1.
|
||||
|
||||
This allows easy use of the result:
|
||||
|
||||
\code
|
||||
QString s;
|
||||
s += QChar::fromUcs4(ch);
|
||||
\endcode
|
||||
|
||||
\code
|
||||
for (char16_t c16 : QChar::fromUcs4(ch))
|
||||
use(c16);
|
||||
\endcode
|
||||
|
||||
\sa fromUcs2(), requiresSurrogates()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn bool QChar::isNull() const
|
||||
|
||||
@ -1564,12 +1604,10 @@ static FullConvertCaseResult fullConvertCase(char32_t uc, QUnicodeTables::Case w
|
||||
auto length = *specialCase++;
|
||||
while (length--)
|
||||
*pp++ = *specialCase++;
|
||||
} else if (Q_UNLIKELY(QChar::requiresSurrogates(uc))) {
|
||||
// so far, case convertion never changes planes (guaranteed by the qunicodetables generator)
|
||||
*pp++ = QChar::highSurrogate(uc);
|
||||
*pp++ = QChar::lowSurrogate(uc + caseDiff);
|
||||
} else {
|
||||
*pp++ = uc + caseDiff;
|
||||
// so far, case convertion never changes planes (guaranteed by the qunicodetables generator)
|
||||
for (char16_t c : QChar::fromUcs4(uc + caseDiff))
|
||||
*pp++ = c;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -2002,14 +2040,10 @@ static void composeHelper(QString *str, QChar::UnicodeVersion version, int from)
|
||||
stcode = ligature;
|
||||
QChar *d = s.data();
|
||||
// ligatureHelper() never changes planes
|
||||
if (QChar::requiresSurrogates(ligature)) {
|
||||
d[starter] = QChar(QChar::highSurrogate(ligature));
|
||||
d[starter + 1] = QChar(QChar::lowSurrogate(ligature));
|
||||
s.remove(i, 2);
|
||||
} else {
|
||||
d[starter] = QChar(ligature);
|
||||
s.remove(i, 1);
|
||||
}
|
||||
int j = 0;
|
||||
for (QChar ch : QChar::fromUcs4(ligature))
|
||||
d[starter + j++] = ch;
|
||||
s.remove(i, j);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -2079,18 +2113,10 @@ static void canonicalOrderHelper(QString *str, QChar::UnicodeVersion version, in
|
||||
QChar *uc = s.data();
|
||||
int p = pos;
|
||||
// exchange characters
|
||||
if (!QChar::requiresSurrogates(u2)) {
|
||||
uc[p++] = QChar(u2);
|
||||
} else {
|
||||
uc[p++] = QChar(QChar::highSurrogate(u2));
|
||||
uc[p++] = QChar(QChar::lowSurrogate(u2));
|
||||
}
|
||||
if (!QChar::requiresSurrogates(u1)) {
|
||||
uc[p++] = QChar(u1);
|
||||
} else {
|
||||
uc[p++] = QChar(QChar::highSurrogate(u1));
|
||||
uc[p++] = QChar(QChar::lowSurrogate(u1));
|
||||
}
|
||||
for (QChar ch : QChar::fromUcs4(u2))
|
||||
uc[p++] = ch;
|
||||
for (QChar ch : QChar::fromUcs4(u1))
|
||||
uc[p++] = ch;
|
||||
if (pos > 0)
|
||||
--pos;
|
||||
if (pos > 0 && s.at(pos).isLowSurrogate())
|
||||
|
@ -120,6 +120,10 @@ public:
|
||||
QT_ASCII_CAST_WARN Q_DECL_CONSTEXPR QChar(uchar c) noexcept : ucs(c) { }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static constexpr QChar fromUcs2(char16_t c) noexcept { return QChar{c}; }
|
||||
static constexpr inline auto fromUcs4(char32_t c) noexcept;
|
||||
|
||||
// Unicode information
|
||||
|
||||
enum Category
|
||||
@ -680,3 +684,5 @@ struct hash<QT_PREPEND_NAMESPACE(QChar)>
|
||||
} // namespace std
|
||||
|
||||
#endif // QCHAR_H
|
||||
|
||||
#include <QtCore/qstringview.h> // for QChar::fromUcs4() definition
|
||||
|
@ -351,6 +351,22 @@ template <typename QStringLike, typename std::enable_if<
|
||||
inline QStringView qToStringViewIgnoringNull(const QStringLike &s) noexcept
|
||||
{ return QStringView(s.data(), s.size()); }
|
||||
|
||||
// QChar inline functions:
|
||||
|
||||
Q_REQUIRED_RESULT constexpr auto QChar::fromUcs4(char32_t c) noexcept
|
||||
{
|
||||
struct R {
|
||||
char16_t chars[2];
|
||||
Q_REQUIRED_RESULT constexpr operator QStringView() const noexcept { return {begin(), end()}; }
|
||||
Q_REQUIRED_RESULT constexpr qsizetype size() const noexcept { return chars[1] ? 2 : 1; }
|
||||
Q_REQUIRED_RESULT constexpr const char16_t *begin() const noexcept { return chars; }
|
||||
Q_REQUIRED_RESULT constexpr const char16_t *end() const noexcept { return begin() + size(); }
|
||||
};
|
||||
return requiresSurrogates(c) ? R{{QChar::highSurrogate(c),
|
||||
QChar::lowSurrogate(c)}} :
|
||||
R{{char16_t(c), u'\0'}} ;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif /* QSTRINGVIEW_H */
|
||||
|
@ -37,6 +37,8 @@ class tst_QChar : public QObject
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void fromChar16_t();
|
||||
void fromUcs4_data();
|
||||
void fromUcs4();
|
||||
void fromWchar_t();
|
||||
void operator_eqeq_null();
|
||||
void operators_data();
|
||||
@ -89,6 +91,34 @@ void tst_QChar::fromChar16_t()
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QChar::fromUcs4_data()
|
||||
{
|
||||
QTest::addColumn<uint>("ucs4");
|
||||
auto row = [](uint ucs4) {
|
||||
QTest::addRow("0x%08X", ucs4) << ucs4;
|
||||
};
|
||||
|
||||
row(0x2f868);
|
||||
row(0x1D157);
|
||||
row(0x1D157);
|
||||
}
|
||||
|
||||
void tst_QChar::fromUcs4()
|
||||
{
|
||||
QFETCH(const uint, ucs4);
|
||||
|
||||
const auto result = QChar::fromUcs4(ucs4);
|
||||
if (QChar::requiresSurrogates(ucs4)) {
|
||||
QCOMPARE(result.chars[0], QChar::highSurrogate(ucs4));
|
||||
QCOMPARE(result.chars[1], QChar::lowSurrogate(ucs4));
|
||||
QCOMPARE(QStringView{result}.size(), 2);
|
||||
} else {
|
||||
QCOMPARE(result.chars[0], ucs4);
|
||||
QCOMPARE(result.chars[1], 0u);
|
||||
QCOMPARE(QStringView{result}.size(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QChar::fromWchar_t()
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
@ -835,13 +865,7 @@ void tst_QChar::normalization_data()
|
||||
for (int j = 0; j < c.size(); ++j) {
|
||||
bool ok;
|
||||
uint uc = c.at(j).toInt(&ok, 16);
|
||||
if (!QChar::requiresSurrogates(uc)) {
|
||||
columns[i].append(QChar(uc));
|
||||
} else {
|
||||
// convert to utf16
|
||||
columns[i].append(QChar(QChar::highSurrogate(uc)));
|
||||
columns[i].append(QChar(QChar::lowSurrogate(uc)));
|
||||
}
|
||||
columns[i].append(QChar::fromUcs4(uc));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user