Qt Weekly #13: QStringLiteral

Published Friday June 13th, 2014 | by

QStringLiteral is a macro that allows you to create QString objects from string literals with little to no runtime overhead. However, its usage can be tricky especially in code that must compile with different compilers, like the Qt code itself. This blog post lists some guidelines to help you avoid the pitfalls.

QString is the ubiquitous representation for a Unicode string in Qt. A common source for a QString are literals hard-coded into the application. In Qt 4, any such creation of a QString from a literal required creating the QString object on the heap, and then translating the literal characters from its run-time character codec to UTF-16. Since Qt 5.0, QStringLiteral allows to avoid this overhead by creating the object and doing the conversion at compile time.

Enable C++11 in your project

QStringLiteral is only optimized if the C++ compiler both supports lambdas and unicode literals. This is the case for Microsoft Visual Studio 2010 and newer, but GCC and Clang need to be told that they should support all C++11 features. When using qmake, this can be achieved by


CONFIG += c++11

Without the features, QStringLiteral falls back to QString::fromUtf8().

Use QStringLiteral only if a QString needs to be created

This sounds trivial, but it’s not necessarily so. Some methods in the Qt API have overloads for either taking a QString, or a QLatin1String object. This is because Latin1 is simpler to parse than UTF-16, and therefore the QLatin1String version can be faster, and use less memory. This is worth remembering especially for some QString methods:


bool same = (str == QLatin1String("Hello"));
str.startsWith(QLatin1String("Hello"));
str += QLatin1String("World");

are more efficient than


bool same = (str == QStringLiteral("Hello"));
str.startsWith(QStringLiteral("Hello"));
str += QStringLiteral("World");

Do not use QStringLiteral for empty strings

Prefer


QString()

over


QStringLiteral("")

The default constructor for QString is cheaper in terms of both instructions and memory.

Avoid duplicated QStringLiterals

Avoid having multiple QStringLiterals with the same content. For plain literals and QLatin1String, compilers try to consolidate identical literals so that they are not duplicated. For QStringLiteral, identical strings cannot be merged.

String concatenation

Unfortunately Visual Studio does not implement concatenation for string literals in the way the C++11 standard says. That is, having multiple adjacent ” ” sections (e.g. useful to spread across multiple lines) won’t work:


// error C2308: concatenating mismatched strings
// Concatenating wide "Hello" with narrow "World"
QStringLiteral("Hello " "World");

Other Visual Studio bugs

There are also other bugs in older Visual Studio versions. Here are a few known ones:


// (Visual Studio 2010, 2012)
// error: C1001: An internal error has occurred in the compiler.
QString array[2] = { QStringLiteral("Hello"), QStringLiteral("World") };


// (Visual Studio 2010)
// C2587: 'holder' : illegal use of local variable as default parameter
static QString formattedAddress(const QString &address,
        const QString &newLine = QStringLiteral(""))
{
  // ...
}

Summary

QStringLiteral speeds up the creation of QStrings from literals, at the expense of bigger binary sizes. To make these optimizations it requires C++11 features. There are cases though where using QStringLiteral does not pay off: For Qt API that optionally takes a QLatin1String, and for empty strings.

If you use QStringLiteral you should avoid declaring the same literal in multiple places: This furthermore blows up the binary sizes. Also, QStringLiteral exposes some bugs in Visual Studio compilers.

Further reading

Olivier Goffart explains both the reasons and the implementation of QStringLiteral in his highly recommended QStringLiteral blog post. Thiago Macieira blogged about it too.

Did you like this? Share it:

Posted in C++, Qt | Tags: , ,

13 comments to Qt Weekly #13: QStringLiteral

Nikita Baryshnikov says:

And also as mentioned in documentation http://qt-project.org/doc/qt-5/qstring.html adding `DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII’ to project files disables automatic conversions from C string literals and pointers to Unicode.

Nikita Baryshnikov says:

Am I right that for given example with QLatin1String
bool same = (str == QLatin1String(“Hello”));
str.startsWith(QLatin1String(“Hello”));
str += QLatin1String(“World”);

I can use QByteArray?:
bool same = (str == QByteArrayLiteral(“Hello”)); // str :: QByteArray
str.startsWith(QByteArrayLiteral(“Hello”));
str += QByteArrayLiteral(“World”);

Kai Koehne says:

Well… you could, unless you specified QT_NO_CAST_FROM_ASCII. But why would you?

The QByteArray overloads in QString will convert the QByteArray to a QString with the help of QString::fromUtf8(). So you don’t gain anything here, except for larger binaries and obfuscating the encoding.

(I’m assuming “str” is a QString. If it’s a QByteArray your solution is obviously right :)

Nikita Baryshnikov says:

Sorry I meant if I sure that my string is latin1 I can store it in QByteArrays and compare them:
QByteArray str = getNiceKeyFor(..);
bool same = (str == QByteArrayLiteral(“Hello”));

But not about comparing QString and QByteArray

Kwabena Agyeman says:

Thanks for the post! A lot of good work has been put into speeding string operations up. But, it’s still a nightmare to try and consistently use QStringLiteral/QLatin1String/etc.

I wonder if a tool like moc could be made that would be capable of finding where constant strings are used and then wrap those strings with QStringLiteral/QLatin1String/etc.

However, I suppose it will be impossible to guess the programmers intention with how a string is used in all cases without near AI smarts in the tool.

Kai Koehne says:

Most of these are just rules of thumb, indeed … anyhow, it’s might be a good idea to include at least some hints into tools like semantic checkers like krazy, or the Qt Creator semantic checks.

For Qt Creator, I just created https://bugreports.qt-project.org/browse/QTCREATORBUG-12424 .

Ville says:

It’s very interesting tip to know that I should “avoid declaring the same literal in multiple places”. But then, how should I handle the situation, when I many times have:

QMap map;
First I need to check if the map has the string key, and then I read it:


if (map.contains(QStringLiteral("key"))
goFish(map[QStringLiteral("key")]);

But now I have declared the key multiple times. Should I declare a QString variable just for this case?


QMap map;
const QString fish(QStringLiteral("key"));
if (map.contains(fish))
goFish(map[fish]);

But thanks for very helpful blog entry.

Kai Koehne says:

This is indeed handled best by a local QString variable.

It can get a bit ugly though if the string literals are used in different methods, especially since QStringLiteral is a non-POD type that we cannot use for global static variables in Qt.

Ville says:

Douh, I should have used code-tags:


QMap map;
const QString fish(QStringLiteral(“key”));
if (map.contains(fish))
goFish(map[fish]);

Seamus says:

Something like the following code should be added to Qt 4 so I can benefit from any performance implovements when building with Qt 5, but keep compatibility.


#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
# ifndef QStringLiteral
# define QStringLiteral(str) QString::fromUtf8("" str "", sizeof(str) - 1)
# endif

# ifndef QByteArrayLiteral
# define QByteArrayLiteral(str) QByteArray(str, sizeof(str) - 1)
# endif
#endif

Kai Koehne says:

The snippet is indeed helpful if your code base must still compile with Qt 4 … anyhow, I personally don’t think this should end up in the Qt 4 headers itself: QStringLiteral is a Qt 5 feature, and masquerading this in Qt 4 does IMO hurt more than it helps.

Andre' says:

Well, it would make porting easier. Provide that as opt-in using some CONFIG += qt5compat doesn’t sound overly wrong to me.

Commenting closed.