Remove a use of a QMap in QAccessibleTextWidget::attributes()

The QMap<QByteArray, QString> was only used to hold key/value pairs
until they were serialized into a QString at the end of the function.

Serialize into QString directly instead, dropping the temporary QMap.

The problem is that we'd like to keep the line formatting central
while at the same time harnessing the power of QStringBuilder.

This is possible with a templated function with an input/output
parameter:

     format_attr(QString &result, const char *key, T &&value)
     format_attr(result, text-foo, str + str2 + str3);

Or with return type deduction:

     formatted(const char *key, T &&value) -> decltype((expr)) { return expr; }
     result += formatted(text-foo, str + str2 + str3);

I don't like out parameters, and we can't rely on auto return
type deduction, yet, so I opted for a miniature expression
template solution that can only match the expression

     attr[key] = value;

where 'key' is a const char* and 'value' can be anything that
QStringBuilder supports. This allows to keep the syntax of a map
while at the same time serializing to QString immediately.

The only behavioral difference to the old code is that the
attributes are no longer sorted, but order doesn't matter.

Saves more than 10KiB in text size on optimized GCC 4.9 Linux
AMD64 builds.

Change-Id: I7b3bec0466ef24156c693adaa95f0316007e0bfe
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
bb10
Marc Mutz 2015-12-19 03:25:39 +01:00
parent 6066af7f09
commit 92398565fc
1 changed files with 49 additions and 7 deletions

View File

@ -707,6 +707,53 @@ int QAccessibleTextWidget::selectionCount() const
return textCursor().hasSelection() ? 1 : 0;
}
namespace {
/*!
\internal
\brief Helper class for AttributeFormatter
This class is returned from AttributeFormatter's indexing operator to act
as a proxy for the following assignment.
It uses perfect forwarding in its assignment operator to amend the RHS
with the formatting of the key, using QStringBuilder. Consequently, the
RHS can be anything that QStringBuilder supports.
*/
class AttributeFormatterRef {
QString &string;
const char *key;
friend class AttributeFormatter;
AttributeFormatterRef(QString &string, const char *key) : string(string), key(key) {}
public:
template <typename RHS>
void operator=(RHS &&rhs)
{ string += QLatin1String(key) + QLatin1Char(':') + std::forward<RHS>(rhs) + QLatin1Char(';'); }
};
/*!
\internal
\brief Small string-builder class that supports a map-like API to serialize key-value pairs.
\code
AttributeFormatter attrs;
attrs["foo"] = QLatinString("hello") + world + QLatin1Char('!');
\endcode
The key type is always \c{const char*}, and the right-hand-side can
be any QStringBuilder expression.
Breaking it down, this class provides the indexing operator, stores
the key in an instance of, and then returns, AttributeFormatterRef,
which is the class that provides the assignment part of the operation.
*/
class AttributeFormatter {
QString string;
public:
AttributeFormatterRef operator[](const char *key)
{ return AttributeFormatterRef(string, key); }
QString toFormatted() const { return string; }
};
} // unnamed namespace
QString QAccessibleTextWidget::attributes(int offset, int *startOffset, int *endOffset) const
{
/* The list of attributes can be found at:
@ -766,7 +813,7 @@ QString QAccessibleTextWidget::attributes(int offset, int *startOffset, int *end
QTextBlockFormat blockFormat = cursor.blockFormat();
QMap<QByteArray, QString> attrs;
AttributeFormatter attrs;
QString family = charFormat.font().family();
if (!family.isEmpty()) {
family = family.replace('\\',QStringLiteral("\\\\"));
@ -856,12 +903,7 @@ QString QAccessibleTextWidget::attributes(int offset, int *startOffset, int *end
break;
}
QString result;
foreach (const QByteArray &attributeName, attrs.keys()) {
result.append(QString::fromLatin1(attributeName)).append(':').append(attrs[attributeName]).append(';');
}
return result;
return attrs.toFormatted();
}
int QAccessibleTextWidget::cursorPosition() const