TSG
TSG

Reputation: 4617

Return a QString from a function - thread safe?

I'm new to Qt - but this may be a very basic c++ issue. I have a simple function that returns a QString:

QString testclass::myfunc(int i)
{
    QString result;
    switch (i) {
    case 1: result = "one"; break;
    case 2: result = "two"; break;
    }
    return result;
}

Is this safe? Does the c compiler ensure the return value stays in memory long enough to be used by the calling function? (Or does this risk memory corruption). If the latter, what is the right way to return a QString? (Does the result var have to be static? Does result have to be a member var of testclass?)

Does it matter that QString contains constants? (What id case 3 assigned result to a random string)

What if myfunc is a static method that I want to call from different threads? Would I have to pass in an extra Qstring by reference to ensure each caller gets their own variable (and return void)?


Here is the actual function (cleaned up a bit) - and note that this function is STATIC in case that makes a difference:

QString L::toString(const L::editions &level)
{
    QString levelStr;
    switch (level) {
        case L::one:
            levelStr = "one";
            break;
        case L::two:
            levelStr = "two";
            break;
        case L::three:
            levelStr = "thre";
            break;
        default:
            levelStr = "Internal Error";
            break;
    }
    return levelStr;
}

and yet valgrind complains (line 121 is 'levelStr = "one";')

34 bytes in 1 blocks are definitely lost in loss record 197 of 716
  in L::toString(L::edition const&) in /mnt/lserver2/data/development/haast/src/linfo.cpp:121
  1: malloc in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so
  2: QArrayData::allocate(unsigned long, unsigned long, unsigned long, QFlags<QArrayData::AllocationOption>) in /opt/Qt/5.3/gcc_64/lib/libQt5Core.so.5.3.1
  3: QString::QString(int, Qt::Initialization) in /opt/Qt/5.3/gcc_64/lib/libQt5Core.so.5.3.1
  4: /opt/Qt/5.3/gcc_64/lib/libQt5Core.so.5.3.1
  5: QString::fromUtf8_helper(char const*, int) in /opt/Qt/5.3/gcc_64/lib/libQt5Core.so.5.3.1
  6: QString::fromUtf8(char const*, int) in <a href="file:///opt/Qt/5.3/gcc_64/include/QtCore/qstring.h:492" >/opt/Qt/5.3/gcc_64/include/QtCore/qstring.h:492</a>
  7: QString::operator=(char const*) in <a href="file:///opt/Qt/5.3/gcc_64/include/QtCore/qstring.h:609" >/opt/Qt/5.3/gcc_64/include/QtCore/qstring.h:609</a>
  8: L::toString(L::Lconst&amp;) in <a 

Upvotes: 3

Views: 8379

Answers (2)

QString is a value class, it'd be useless if you couldn't return it from a function. It's no different here than std::string would be. Both can be used the way you demonstrate.

The concept of a "const string" that you refer to is imaginary. There's no such thing. The statement result = "foo"; doesn't produce a string that's somehow special. This is not C, you're not returning a const char * - and for a good reason.

The thread-safety aspect has not much to do with strings. The method body that you show could be a static method, since it doesn't use any of the instance's data. It is also a pure function, having no access to any shared state at all. Such pure functions are thread-safe by definition, since they don't access shared state. You might wish to revise your question with an example that's closer to your problem and actually demonstrates some threading issues.

QString, like other Qt's implicitly shared value classes, is thread-safe as long as a particular instance of it is accessed from one thread only. It doesn't matter that the instance might have been assigned from or copied from another string instance. For example:

QString a = "foo";
{
  QFutureSynchronizer sync;
  sync.addFuture(QtConcurrent::run([&a] { a.append("bar"); }));
  sync.addFuture(QtConcurrent::run([&a] { a.append("baz"); }));
}
// This is not thread safe: a might be concurrently accessed from two
// threads. The behavior is undefined. It might format your hard drive.

QString c = "Homer";
QString d = c;
{
  QFutureSynchronizer sync;
  sync.addFuture(QtConcurrent::run([&c] { c.append("'s"); }));
  sync.addFuture(QtConcurrent::run([&d] { d.append(" and Elmer's"; }));
}
// This is thread safe. One of the threads will safely deep-copy the
// c's contents.
Q_ASSERT(c == "Homer's");
Q_ASSERT(d == "Homer and Elmer's");

Upvotes: 1

phyatt
phyatt

Reputation: 19112

http://qt-project.org/doc/qt-5/qstring.html#details

The QString class provides a Unicode character string.

QString stores a string of 16-bit QChars, where each QChar corresponds one Unicode 4.0 character. (Unicode characters with code values above 65535 are stored using surrogate pairs, i.e., two consecutive QChars.)

Unicode is an international standard that supports most of the writing systems in use today. It is a superset of US-ASCII (ANSI X3.4-1986) and Latin-1 (ISO 8859-1), and all the US-ASCII/Latin-1 characters are available at the same code positions.

Behind the scenes, QString uses implicit sharing (copy-on-write) to reduce memory usage and to avoid the needless copying of data. This also helps reduce the inherent overhead of storing 16-bit characters instead of 8-bit characters.

In addition to QString, Qt also provides the QByteArray class to store raw bytes and traditional 8-bit '\0'-terminated strings. For most purposes, QString is the class you want to use. It is used throughout the Qt API, and the Unicode support ensures that your applications will be easy to translate if you want to expand your application's market at some point. The two main cases where QByteArray is appropriate are when you need to store raw binary data, and when memory conservation is critical (like in embedded systems).

Basically QString is awesome and almost worry free. You can use it wherever, and however you like. If you are running into any sort of slow down from appending strings too often, there is a special way to use a string builder, but in my experience, there are plenty of other places to improve before trying to make QString better.

And to answer your questions directly:

Is this safe? Does the c compiler ensure the return value stays in memory long enough to be used by the calling function? (Or does this risk memory corruption). If the latter, what is the right way to return a QString? (Does the result var have to be static? Does result have to be a member var of testclass?)

In all of the above cases mentioned above, it is safe. Shared pointers and the like keep it in memory as long as any function has a handle to the QString. Once it goes completely out of scope, it will clean itself up.

Does it matter that QString contains constants? (What id case 3 assigned result to a random string)

No, it doesn't matter.

What if myfunc is a static method that I want to call from different threads? Would I have to pass in an extra Qstring by reference to ensure each caller gets their own variable (and return void)?

You should wrap it with cross thread protection, such as a QMutexLocker.

UPDATE: QMutexLocker example

// In your constructor

m_mutex = new QMutex();


// When accessing a shared element across threads

{
    QMutexLocker locker(m_mutex);
    // Accessing a variable is now threadsafe!
    m_sharedDataString += "!";
}

Hope that helps.

Upvotes: 5

Related Questions