I/O In Qt

Qt encapsulates the more generalized notion of byte streams in its QIODevice class, which is the parent class for QFile, as well as network I/O classes such as QTcpSocket. We don’t directly create a QIODevice instance, of course, but instead create something such as a QFile subclass and then work with the QFile instance directly to read from and write to the file.

Before accessing the device, open() must be called to set the correct OpenMode (such as ReadOnly or ReadWrite). You can then write to the device with write() or putChar(), and read by calling either read(), readLine(), or readAll(). Call close() when you are done with the device.

QIODevice distinguishes between two types of devices: random-access devices and sequential devices.

  • Random-access devices support seeking to arbitrary positions using seek(). The current position in the file is available by calling pos(). QFile and QBuffer are examples of random-access devices.
  • Sequential devices don’t support seeking to arbitrary positions. The data must be read in one pass. The functions pos() and size() don’t work for sequential devices. QTcpSocket and QProcess are examples of sequential devices.

You can use isSequential() to determine the type of device.

QIODevice emits readyRead() when new data is available for reading; for example, if new data has arrived on the network or if additional data is appended to a file that you are reading from. You can call bytesAvailable() to determine the number of bytes that are currently available for reading. It’s common to use bytesAvailable() together with the readyRead() signal when programming with asynchronous devices such as QTcpSocket, where fragments of data can arrive at arbitrary points in time. QIODevice emits the bytesWritten() signal every time a payload of data has been written to the device. Use bytesToWrite() to determine the current amount of data waiting to be written.

Certain subclasses of QIODevice, such as QTcpSocket and QProcess, are asynchronous. This means that I/O functions such as write() or read() always return immediately, while communication with the device itself may happen when control goes back to the event loop. QIODevice provides functions that allow you to force these operations to be performed immediately, while blocking the calling thread and without entering the event loop. This allows QIODevice subclasses to be used without an event loop, or in a separate thread:

  • waitForReadyRead() – This function suspends operation in the calling thread until new data is available for reading.
  • waitForBytesWritten() – This function suspends operation in the calling thread until one payload of data has been written to the device.
  • waitFor….() – Subclasses of QIODevice implement blocking functions for device-specific operations. For example, QProcess has a function called waitForStarted() which suspends operation in the calling thread until the process has started.

Calling these functions from the main, GUI thread, may cause your user interface to freeze. Example:

QProcess gzip;
gzip.start("gzip", QStringList() << "-c");
if (!gzip.waitForStarted())
    return false;

gzip.write("uncompressed data");

QByteArray compressed;
while (gzip.waitForReadyRead())
    compressed += gzip.readAll();

By subclassing QIODevice, you can provide the same interface to your own I/O devices. Subclasses of QIODevice are only required to implement the protected readData() and writeData() functions. QIODevice uses these functions to implement all its convenience functions, such as getChar(), readLine() and write(). QIODevice also handles access control for you, so you can safely assume that the device is opened in write mode if writeData() is called.

Some subclasses, such as QFile and QTcpSocket, are implemented using a memory buffer for intermediate storing of data. This reduces the number of required device accessing calls, which are often very slow. Buffering makes functions like getChar() and putChar() fast, as they can operate on the memory buffer instead of directly on the device itself. Certain I/O operations, however, don’t work well with a buffer. For example, if several users open the same device and read it character by character, they may end up reading the same data when they meant to read a separate chunk each. For this reason, QIODevice allows you to bypass any buffering by passing the Unbuffered flag to open(). When subclassing QIODevice, remember to bypass any buffer you may use when the device is open in Unbuffered mode.

Some sequential devices support communicating via multiple channels. These channels represent separate streams of data that have the property of independently sequenced delivery. Once the device is opened, you can determine the number of channels by calling the readChannelCount() and writeChannelCount() functions. To switch between channels, call setCurrentReadChannel() and setCurrentWriteChannel(), respectively. QIODevice also provides additional signals to handle asynchronous communication on a per-channel basis.

C++ String

A string is very similar to a std::vector
The following code snippet has the std::string name with the value RainerGrimm. I use the STL algorithm std::find_if to get the upper letter and then extract my first and last name into the variables firstName and lastName. The expression name.begin()+1 shows, that strings support random access iterators:

#include <algorithm>
#include <string>

std::string name{"RainerGrimm"};
auto strIt= std::find_if(name.begin()+1, name.end(),
                       [](char c){ return std::isupper(c); });
if (strIt != name.end()){
 firstName= std::string(name.begin(), strIt);
 lastName= std::string(strIt, name.end());
}

for multi byte string you can use basic_string

typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
typedef basic_string<char16_t> u16string;
typedef basic_string<char32_t> u32string;

create string

std::string defaultString;
std::string other{"123456789"};
std::string str1(other);                      // 123456789
std::string tmp(other);                       // 123456789
std::string str2(std::move(tmp));             // 123456789
std::string str3(other.begin(), other.end()); // 123456789
std::string str4(other, 2);                   // 3456789
std::string str5(other, 2, 5);                // 34567
std::string str6("123456789", 5);             // 12345
std::string str7(5, '1');                     // 11111
std::string str8({'1', '2', '3', '4', '5'});  // 12345
std::cout << str6.substr();                   // 12345
std::cout << str6.substr(1);                  // 2345
std::cout << str6.substr(1, 2);               // 23
MethodsDescription
str.empty()Checks if str has elements.
str.size(), str.length()Number of elements of the str.
str.capacity()Number of elements str can have without reallocation.
str.max_size()Number of elements str can maximal have.
str.resize(n)Increases str to n elements.
str.reserve(n)Reserve memory for a least n elements.
str.shrink_to_fit()Adjusts the capacity of the string to it’s size.
size and capacity
void showStringInfo(const std::string& s){
  std::cout << s << ": ";
  std::cout << s.size() << " ";
  std::cout << s.capacity() << " ";
  std::cout << s.max_size() << " ";
}

std::string str;
showStringInfo(str);  // "": 0 0 4611686018427387897

str +="12345";
showStringInfo(str);  // "12345": 5 5 4611686018427387897

str.resize(30);
showStringInfo(str);  // "12345": 30 30 4611686018427387897

str.reserve(1000);
showStringInfo(str);  // "12345": 30 1000 4611686018427387897

str.shrink_to_fit();
showStringInfo(str);  // "12345": 30 30 4611686018427387897

Elements Access

std::string str= {"0123456789"};
std::cout << str.front() << std::endl;           // 0
std::cout << str.back() << std::endl;            // 9
for (int i= 0; i <= 3; ++i){
  std::cout << "str[" << i << "]:" << str[i] << "; ";
} // str[0]: 0; str[1]: 1; str[2]: 2; str[3]: 3;

std::cout << str[10] << std::endl;               // undefined behaviour
try{
  str.at(10);
}
catch (const std::out_of_range& e){
  std::cerr << "Exception: " << e.what() << std::endl;
} // Exception: basic_string::at

std::cout << *(&str[0]+5) << std::endl;         // 5
std::cout << *(&str[5]) << std::endl;           // 5
std::cout << str[5] << std::endl;               // 5

search

str= {"dkeu84kf8k48kdj39kdj74945du942"};
std::string str2{"84"};

std::cout << str.find('8');                              // 4
std::cout << str.rfind('8');                             // 11
std::cout << str.find('8', 10);                          // 11
std::cout << str.find(str2);                             // 4
std::cout << str.rfind(str2);                            // 4
std::cout << str.find(str2, 10);                         // 18446744073709551615

str2="0123456789";

std::cout << str.find_first_of("678");                    // 4
std::cout << str.find_last_of("678");                     // 20
std::cout << str.find_first_of("678", 10);                // 11
std::cout << str.find_first_of(str2);                     // 4
std::cout << str.find_last_of(str2);                      // 29
std::cout << str.find_first_of(str2, 10);                 // 10
std::cout << str.find_first_not_of("678");                // 0
std::cout << str.find_last_not_of("678");                 // 29
std::cout << str.find_first_not_of("678", 10);            // 10
std::cout << str.find_first_not_of(str2);                 // 0
std::cout << str.find_last_not_of(str2);                  // 26
std::cout << str.find_first_not_of(str2, 10);             // 12
MethodsDescription
str= str2Assigns str2 to str.
str.assign(...)Assigns to stra new string.
str.swap(str2)Swaps str and str2.
str.pop_back()Removes the last character from str.
str.erase(...)Removes characters from str.
str.clear()Clears the str.
str.append(...)Appends characters to str.
str.push_back(s)Appends the character s to str.
str.insert(pos, ...)Inserts characters in str starting at pos.
str.replace(pos, len, ...)Replaces the len characters from str starting at pos
modifying functions
MethodDescription
std::to_string(val)Converts val into a std::string.
std::to_wstring(val)Converts val into a std::wstring.
std::stoi(str)Returns an int value.
std::stol(str)Returns a long value.
std::stoll(str)Returns a long long value.
std::stoul(str)Returns an unsigned long value.
std::stoull(str)Returns an unsigned long long value.
std::stof(str)Returns a float value.
std::stod(str)Returns a double value.
std::stold(str)Returns an long double value.
numeric converting

Regular Expression

typedef basic_regex<char> regex;
typedef basic_regex<wchar_t> wregex;

search

#include <regex>
...
using std::regex_constants::ECMAScript;
using std::regex_constants::icase;

std::string theQuestion="C++ or c++, that's the question.";
std::string regExprStr(R"(c\+\+)");

std::regex rgx(regExprStr);
std::smatch smatch;

if (std::regex_search(theQuestion, smatch, rgx)){
  std::cout << "case sensitive: " << smatch[0];       // c++
}

std::regex rgxIn(regExprStr, ECMAScript|icase);
if (std::regex_search(theQuestion, smatch, rgxIn)){
  std::cout << "case insensitive: " << smatch[0];     // C++
}
#include <regex>
...

// regular expression holder for time
std::regex crgx("([01]?[0-9]|2[0-3]):[0-5][0-9]");

// const char*
std::cmatch cmatch;

const char* ctime{"Now it is 23:10."};
if (std::regex_search(ctime, cmatch, crgx)){
  std::cout << ctime << std::endl;                   // Now it is 23:10.   
  std::cout << "Time: " << cmatch[0] << std::endl;   // Time: 23:10
}

// std::string
std::smatch smatch;

std::string stime{"Now it is 23:25."};
if (std::regex_search(stime, smatch, crgx)){
  std::cout << stime << std::endl;                   // Now it is 23:25.
  std::cout << "Time: " << smatch[0] << std::endl;   // Time: 23:25
}

// regular expression holder for time
std::wregex wrgx(L"([01]?[0-9]|2[0-3]):[0-5][0-9]");

// const wchar_t*
std::wcmatch wcmatch;

const wchar_t* wctime{L"Now it is 23:47."};
if (std::regex_search(wctime, wcmatch, wrgx)){
  std::wcout << wctime << std::endl;                 // Now it is 23:47.
  std::wcout << "Time: " << wcmatch[0] << std::endl; // Time: 23:47
}

// std::wstring
std::wsmatch wsmatch;

std::wstring  wstime{L"Now it is 00:03."};
if (std::regex_search(wstime, wsmatch, wrgx)){
  std::wcout << wstime << std::endl;                  // Now it is 00:03.
  std::wcout << "Time: " << wsmatch[0] << std::endl;  // Time: 00:03
}

    QByteArray bytes="hello world 112";
    std::regex reg("((\\s+)|([a-z]+)|(\\d+))+");
    std::match_results<QByteArray::iterator> matches;
    std::regex_search(bytes.begin(),bytes.end(),matches,reg);
    for(auto& v : matches){
        qDebug()<<v.first;
    }

match

#include <regex>
...

std::string numberRegEx(R"([-+]?([0-9]*\.[0-9]+|[0-9]+))");
std::regex rgx(numberRegEx);
const char* numChar{"2011"};

if (std::regex_match(numChar, rgx)){
  std::cout << numChar << " is a number." << std::endl;
}                                            // 2011 is a number.

const std::string numStr{"3.14159265359"};
if (std::regex_match(numStr, rgx)){
  std::cout << numStr << " is a number." << std::endl;
}                                           // 3.14159265359 is a number.

const std::vector<char> numVec{{'-', '2', '.', '7', '1', '8', '2',
                                '8', '1', '8', '2', '8'}};
if (std::regex_match(numVec.begin(), numVec.end(), rgx)){
    for (auto c: numVec){ std::cout << c ;};
    std::cout << " is a number." << std::endl;
}                                           // -2.718281828 is a number.

replace

string future{"Future"};
string unofficialName{"The unofficial name of the new C++ standard is C++0x."};

regex rgxCpp{R"(C\+\+0x)"};
string newCppName{"C++11"};
string newName{regex_replace(unofficialName, rgxCpp, newCppName)};

regex rgxOff{"unofficial"};
string makeOfficial{"official"};
string officialName{regex_replace(newName, rgxOff, makeOfficial)};

cout << officialName << endl;
                          // The official name of the new C++ standard is C++11.

filter

std::string future{"Future"};
const std::string unofficial{"unofficial, C++0x"};
const std::string official{"official, C++11"};

std::regex regValues{"(.*),(.*)"};
std::string standardText{"The $1 name of the new C++ standard is $2."};
std::string textNow= std::regex_replace(unofficial, regValues, standardText);
std::cout << textNow << std::endl;
                        // The unofficial name of the new C++ standard is C++0x.

std::smatch smatch;
if (std::regex_match(official, smatch, regValues)){
  std::cout << smatch.str();                                   // official,C++11
  std::string textFuture= smatch.format(standardText);
  std::cout << textFuture << std::endl;
}                       // The official name of the new C++ standard is C++11.

Multithreading With Qt

QThread

A QThread object manages one thread of control within the program. QThreads begin executing in run(). By default, run() starts the event loop by calling exec() and runs a Qt event loop inside the thread.

class MyThread : public QThread 
{ 
    Q_OBJECT 
    void run() Q_DECL_OVERRIDE
    { 
        /* perform the expensive operation */ 
    } 
}; 
 
void MyObject::startWorkInAThread() 
{ 
    MyThread *myThread = new MyThread(this); 
    connect(myThread, &MyObject::threadFinished, this, 
        MyObject::notifyThreadFinished); 
    connect(myThread, &MyThread::finished, myThread, 
        &QObject::deleteLater); 
    myThread->start(); 
}

You can use worker objects by moving them to the thread using QObject::moveToThread().

class Worker : public QObject
{
    Q_OBJECT

public slots:
    void doWork(const QString ¶meter) {
        QString result;
        /* ... here is the expensive or blocking operation ... */
        emit resultReady(result);
    }

signals:
    void resultReady(const QString &result);
};

class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;
public:
    Controller() {
        Worker *worker = new Worker;
        worker->moveToThread(&workerThread);
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
        connect(this, &Controller::operate, worker, &Worker::doWork);
        connect(worker, &Worker::resultReady, this, &Controller::handleResults);
        workerThread.start();
    }
    ~Controller() {
        workerThread.quit();
        workerThread.wait();
    }
public slots:
    void handleResults(const QString &);
signals:
    void operate(const QString &);
};

The code inside the Worker’s slot would then execute in a separate thread. However, you are free to connect the Worker’s slots to any signal, from any object, in any thread. It is safe to connect signals and slots across different threads, thanks to a mechanism called queued connections.

class WorkerThread : public QThread
{
    Q_OBJECT
    void run() override {
        QString result;
        /* ... here is the expensive or blocking operation ... */
        emit resultReady(result);
    }
signals:
    void resultReady(const QString &s);
};

void MyObject::startWorkInAThread()
{
    WorkerThread *workerThread = new WorkerThread(this);
    connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
    connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
    workerThread->start();
}

In that example, the thread will exit after the run function has returned. There will not be any event loop running in the thread unless you call exec().

Managing Threads

QThread will notifiy you via a signal when the thread is started() and finished(), or you can use isFinished() and isRunning() to query the state of the thread.

You can stop the thread by calling exit() or quit(). In extreme cases, you may want to forcibly terminate() an executing thread. However, doing so is dangerous and discouraged. Please read the documentation for terminate() and setTerminationEnabled() for detailed information.

From Qt 4.8 onwards, it is possible to deallocate objects that live in a thread that has just ended, by connecting the finished() signal to QObject::deleteLater().

Use wait() to block the calling thread, until the other thread has finished execution (or until a specified time has passed).

QThread also provides static, platform independent sleep functions: sleep(), msleep(), and usleep() allow full second, millisecond, and microsecond resolution respectively. These functions were made public in Qt 5.0.

Note: wait() and the sleep() functions should be unnecessary in general, since Qt is an event-driven framework. Instead of wait(), consider listening for the finished() signal. Instead of the sleep() functions, consider using QTimer.

The static functions currentThreadId() and currentThread() return identifiers for the currently executing thread. The former returns a platform specific ID for the thread; the latter returns a QThread pointer.

To choose the name that your thread will be given (as identified by the command ps -L on Linux, for example), you can call setObjectName() before starting the thread. If you don’t call setObjectName(), the name given to your thread will be the class name of the runtime type of your thread object (for example, "RenderThread" in the case .

QThreadPool

QThreadPool manages and recyles individual QThread objects to help reduce thread creation costs in programs that use threads. Each Qt application has one global QThreadPool object, which can be accessed by calling globalInstance().

To use one of the QThreadPool threads, subclass QRunnable and implement the run() virtual function. Then create an object of that class and pass it to QThreadPool::start().

class HelloWorldTask : public QRunnable
{
    void run() override
    {
        qDebug() << "Hello world from thread" << QThread::currentThread();
    }
};

HelloWorldTask *hello = new HelloWorldTask();
// QThreadPool takes ownership and deletes 'hello' automatically
QThreadPool::globalInstance()->start(hello);

QThreadPool deletes the QRunnable automatically by default. Use QRunnable::setAutoDelete() to change the auto-deletion flag.

QThreadPool supports executing the same QRunnable more than once by calling tryStart(this) from within QRunnable::run(). If autoDelete is enabled the QRunnable will be deleted when the last thread exits the run function. Calling start() multiple times with the same QRunnable when autoDelete is enabled creates a race condition and is not recommended.

Threads that are unused for a certain amount of time will expire. The default expiry timeout is 30000 milliseconds (30 seconds). This can be changed using setExpiryTimeout(). Setting a negative expiry timeout disables the expiry mechanism.

Call maxThreadCount() to query the maximum number of threads to be used. If needed, you can change the limit with setMaxThreadCount(). The default maxThreadCount() is QThread::idealThreadCount(). The activeThreadCount() function returns the number of threads currently doing work.

The reserveThread() function reserves a thread for external use. Use releaseThread() when your are done with the thread, so that it may be reused. Essentially, these functions temporarily increase or reduce the active thread count and are useful when implementing time-consuming operations that are not visible to the QThreadPool.

Note that QThreadPool is a low-level class for managing threads, see the Qt Concurrent module for higher level alternatives.

note: you can create runnable object by QRunnable::create(std::function<void ()> functionToRun)

Mutex and Recursive Mutix

it is very similar to c++ mutex

QMutex mutex;
int number = 6;

void method1()
{
    mutex.lock();
    number *= 5;
    number /= 4;
    mutex.unlock();
}

void method2()
{
    mutex.lock();
    number *= 3;
    number /= 2;
    mutex.unlock();
}

Then only one thread can modify number at any given time and the result is correct. This is a trivial example, of course, but applies to any other case where things need to happen in a particular sequence.

When you call lock() in a thread, other threads that try to call lock() in the same place will block until the thread that got the lock calls unlock(). A non-blocking alternative to lock() is tryLock().

QMutex is optimized to be fast in the non-contended case. A non-recursive QMutex will not allocate memory if there is no contention on that mutex. It is constructed and destroyed with almost no overhead, which means it is fine to have many mutexes as part of other classes.

The QRecursiveMutex class is a mutex, like QMutex, with which it is API-compatible. It differs from QMutex by accepting lock() calls from the same thread any number of times. QMutex would deadlock in this situation.

QRecursiveMutex is much more expensive to construct and operate on, so use a plain QMutex whenever you can. 

int complexFunction(int flag)
{
    QMutexLocker locker(&mutex);

    int retVal = 0;

    switch (flag) {
    case 0:
    case 1:
        return moreComplexFunction(flag);
    case 2:
        {
            int status = anotherFunction();
            if (status < 0)
                return -2;
            retVal = status + flag;
        }
        break;
    default:
        if (flag > 10)
            return -1;
        break;
    }

    return retVal;
}

QMutexLocker is similar to c++ std::unique_lock

QReadWriteLock lock;

void ReaderThread::run()
{
    ...
    lock.lockForRead();
    read_file();
    lock.unlock();
    ...
}

void WriterThread::run()
{
    ...
    lock.lockForWrite();
    write_file();
    lock.unlock();
    ...
}

A read-write lock is a synchronization tool for protecting resources that can be accessed for reading and writing. This type of lock is useful if you want to allow multiple threads to have simultaneous read-only access, but as soon as one thread wants to write to the resource, all other threads must be blocked until the writing is complete.

In many cases, QReadWriteLock is a direct competitor to QMutex. QReadWriteLock is a good choice if there are many concurrent reads and writing occurs infrequently.

QSemaphore 

A semaphore is a generalization of a mutex. While a mutex can only be locked once, it’s possible to acquire a semaphore multiple times. Semaphores are typically used to protect a certain number of identical resources.

Semaphores support two fundamental operations, acquire() and release():

  • acquire(n) tries to acquire n resources. If there aren’t that many resources available, the call will block until this is the case.
  • release(n) releases n resources.

There’s also a tryAcquire() function that returns immediately if it cannot acquire the resources, and an available() function that returns the number of available resources at any time.

QSemaphore sem(5);      // sem.available() == 5

sem.acquire(3);         // sem.available() == 2
sem.acquire(2);         // sem.available() == 0
sem.release(5);         // sem.available() == 5
sem.release(5);         // sem.available() == 10

sem.tryAcquire(1);      // sem.available() == 9, returns true
sem.tryAcquire(250);    // sem.available() == 9, returns false

https://doc.qt.io/qt-5/qtcore-threads-semaphores-example.html

Wait Conditions

const int DataSize = 100000;

const int BufferSize = 8192;
char buffer[BufferSize];

QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;
class Producer : public QThread
{
public:
    Producer(QObject *parent = NULL) : QThread(parent)
    {
    }

    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            mutex.lock();
            if (numUsedBytes == BufferSize)
                bufferNotFull.wait(&mutex);
            mutex.unlock();

            buffer[i % BufferSize] = "ACGT"[QRandomGenerator::global()->bounded(4)];

            mutex.lock();
            ++numUsedBytes;
            bufferNotEmpty.wakeAll();
            mutex.unlock();
        }
    }
};
class Consumer : public QThread
{
    Q_OBJECT
public:
    Consumer(QObject *parent = NULL) : QThread(parent)
    {
    }

    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            mutex.lock();
            if (numUsedBytes == 0)
                bufferNotEmpty.wait(&mutex);
            mutex.unlock();

            fprintf(stderr, "%c", buffer[i % BufferSize]);

            mutex.lock();
            --numUsedBytes;
            bufferNotFull.wakeAll();
            mutex.unlock();
        }
        fprintf(stderr, "\n");
    }

signals:
    void stringConsumed(const QString &text);
};
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    Producer producer;
    Consumer consumer;
    producer.start();
    consumer.start();
    producer.wait();
    consumer.wait();
    return 0;
}

Thread safety vs Reentrancy

class Counter
{
public:
    Counter() { n = 0; }

    void increment() { ++n; }
    void decrement() { --n; }
    int value() const { return n; }

private:
    int n;
};
class Counter
{
public:
    Counter() { n = 0; }

    void increment() { QMutexLocker locker(&mutex); ++n; }
    void decrement() { QMutexLocker locker(&mutex); --n; }
    int value() const { QMutexLocker locker(&mutex); return n; }

private:
    mutable QMutex mutex;
    int n;
};

Qt Concurrent and futures

it is similar to std::async (function)

QFuture<void> future = QtConcurrent::run(aFunction);

This will run aFunction in a separate thread obtained from the default QThreadPool. You can use the QFuture and QFutureWatcher classes to monitor the status of the function.
To use a dedicated thread pool, you can pass the QThreadPool as the first argument:

extern void aFunction();
QThreadPool pool;
QFuture<void> future = QtConcurrent::run(&pool, aFunction);

Passing arguments to the function is done by adding them to the QtConcurrent::run() call immediately after the function name. For example:

extern void aFunctionWithArguments(int arg1, double arg2, const QString &string);

int integer = ...;
double floatingPoint = ...;
QString string = ...;

QFuture<void> future = QtConcurrent::run(aFunctionWithArguments, integer, floatingPoint, string);

Any return value from the function is available via QFuture:

extern QString functionReturningAString();
QFuture<QString> future = QtConcurrent::run(functionReturningAString);
...
QString result = future.result();
extern QString someFunction(const QByteArray &input);

QByteArray bytearray = ...;

QFuture<QString> future = QtConcurrent::run(someFunction, bytearray);
...
QString result = future.result();

Note that the QFuture::result() function blocks and waits for the result to become available. Use QFutureWatcher to get notification when the function has finished execution and the result is available.

// call 'QList<QByteArray>  QByteArray::split(char sep) const' in a separate thread
QByteArray bytearray = "hello world";
QFuture<QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');
...
QList<QByteArray> result = future.result();

call member function

// call 'void QImage::invertPixels(InvertMode mode)' in a separate thread
QImage image = ...;
QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels, QImage::InvertRgba);
...
future.waitForFinished();
// At this point, the pixels in 'image' have been inverted
QFuture<void> future = QtConcurrent::run([=]() {
    // Code in this block will run in another thread
});

Concurrent Map

QtConcurrent::mapped() takes an input sequence and a map function. This map function is then called for each item in the sequence, and a new sequence containing the return values from the map function is returned.

QImage scaled(const QImage &image)
{
    return image.scaled(100, 100);
}

QList<QImage> images = ...;
QFuture<QImage> thumbnails = QtConcurrent::mapped(images, scaled);

The results of the map are made available through QFuture. See the QFuture and QFutureWatcher documentation for more information on how to use QFuture in your applications.

If you want to modify a sequence in-place, use QtConcurrent::map(). The map function must then be of the form:

void scale(QImage &image)
{
    image = image.scaled(100, 100);
}

QList<QImage> images = ...;
QFuture<void> future = QtConcurrent::map(images, scale);
void addToCollage(QImage &collage, const QImage &thumbnail)
{
    QPainter p(&collage);
    static QPoint offset = QPoint(0, 0);
    p.drawImage(offset, thumbnail);
    offset += ...;
}

QList<QImage> images = ...;
QFuture<QImage> collage = QtConcurrent::mappedReduced(images, scaled, addToCollage);

The reduce function will be called once for each result returned by the map function, and should merge the intermediate into the result variable. QtConcurrent::mappedReduced() guarantees that only one thread will call reduce at a time, so using a mutex to lock the result variable is not necessary. The QtConcurrent::ReduceOptions enum provides a way to control the order in which the reduction is done. If QtConcurrent::UnorderedReduce is used (the default), the order is undefined, while QtConcurrent::OrderedReduce ensures that the reduction is done in the order of the original sequence.

Blocking Variants :Each of the above functions has a blocking variant that returns the final result instead of a QFuture. You use them in the same way as the asynchronous variants.

QList<QImage> images = ...;

// Each call blocks until the entire operation is finished.
QList<QImage> future = QtConcurrent::blockingMapped(images, scaled);

QtConcurrent::blockingMap(images, scale);

QImage collage = QtConcurrent::blockingMappedReduced(images, scaled, addToCollage);

very useful links
QFuture QFutureWatcher

JSON With Qt

QJsonDocument
The QJsonDocument class provides a way to read and write JSON documents.
isArray,isObject,isNull,isEmpty to check the main content of document
object(),array() to get main QJsonObject or QJsonArray
toJson() convert it to QByteArray (json string)
QJsonObject
A JSON object is a list of key value pairs, where the keys are unique strings and the values are represented by a QJsonValue.
A QJsonObject can be converted to and from a QVariantMap. You can query the number of (key, value) pairs with size(), insert(), and remove() entries from it and iterate over its content using the standard C++ iterator pattern.
QJsonArray
A JSON array is a list of values. The list can be manipulated by inserting and removing QJsonValue‘s from the array.
A QJsonArray can be converted to and from a QVariantList. You can query the number of entries with size(), insert(), and removeAt() entries from it and iterate over its content using the standard C++ iterator pattern.
QJsonValue
A value in JSON can be one of 6 basic types:
JSON is a format to store structured data. It has 6 basic data types:bool QJsonValue::Bool
double QJsonValue::Double
string QJsonValue::String
array QJsonValue::Array
object QJsonValue::Object
null QJsonValue::Null
A value can represent any of the above data types. In addition, QJsonValue has one special flag to represent undefined values. This can be queried with isUndefined().
The type of the value can be queried with type() or accessors like isBool(), isString(), and so on. Likewise, the value can be converted to the type stored in it using the toBool(), toString() and so on.
Values are strictly typed internally and contrary to QVariant will not attempt to do any implicit type conversions. This implies that converting to a type that is not stored in the value will return a default constructed return value.
you can use fromVariant(const QVariant &variant) to QJsonValue from QVariant

{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 27,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [],
  "spouse": null
}
QFile file("json.txt");
file.open(QFile::ReadOnly);
QByteArray data=file.readAll();
QJsonObject mainObject=QJsonDocument::fromJson(data).object();
qDebug()<<"first name : "<<(*mainObject.find("firstName")).toString();
qDebug()<<"address  : "<<mainObject["address"].toObject()["streetAddress"].toString();
qDebug()<<"phone number  : "<<mainObject["phoneNumbers"].toArray()[0].toObject()["number"].toString();
first name :  "John"
address  :  "21 2nd Street"
phone number  :  "212 555-1234"

Multithreading

Atomic Data Types

C++ has a set of simple atomic data types. These are booleans, characters, numbers and pointers in many variants. They need the header <atomic>. You can define your atomic data type with the class template std::atomic, but there are serious restrictions for your type std::atomic<MyType>:

For MyType there are the following restrictions:

  • The copy assignment operator for MyType, for all base classes of MyType and all non-static members of MyType, must be trivial. Only a compiler generated copy assignment operator is trivial.
  • MyType must not have virtual methods or base classes.
  • MyType must be bitwise copyable and comparable so that the C functions memcpy or memcmp can be applied.

Atomic data types have atomic operations. For example load and store:

int cnt = 0;
auto f = [&]{cnt++;};
std::thread t1{f}, t2{f}, t3{f}; // undefined behavior

std::atomic<int> cnt{0};
auto f = [&]{cnt++;};
std::thread t1{f}, t2{f}, t3{f}; // OK

what is trivial ?

trivial type is a type whose storage is contiguous (trivially copyable) and which only supports static default initialization (trivially default constructible), either cv-qualified or not. It includes scalar typestrivial classes and arrays of any such types.

trivial class is a class (defined with class, struct or union) that is both trivially default constructible and trivially copyable, which implies that:

  • uses the implicitly defined default, copy and move constructors, copy and move assignments, and destructor.
  • has no virtual members.
  • has no non-static data members with brace- or equal- initializers.
  • its base class and non-static data members (if any) are themselves also trivial types.

is_trivial inherits from integral_constant as being either true_type or false_type, depending on whether T is a trivial type.

class A {};
class B { B() {} };
class C : B {};
class D { virtual void fn() {} };

int main() {
  std::cout << std::boolalpha;
  std::cout << "is_trivial:" << std::endl;
  std::cout << "int: " << std::is_trivial<int>::value << std::endl;
  std::cout << "A: " << std::is_trivial<A>::value << std::endl;
  std::cout << "B: " << std::is_trivial<B>::value << std::endl;
  std::cout << "C: " << std::is_trivial<C>::value << std::endl;
  std::cout << "D: " << std::is_trivial<D>::value << std::endl;
  return 0;
}
is_trivial:
int: true
A: true
B: false
C: false
D: false

Threads

A thread std::thread represents an executable unit. This executable unit, which the thread immediately starts, gets its work package as a callable unit. A callable unit can be a function, a function object or a lambda function

#include <thread>
...
using namespace std;

void helloFunction(){
  cout << "function" << endl;
}

class HelloFunctionObject {
public:
  void operator()() const {
    cout << "function object" << endl;
  }
};

thread t1(helloFunction);                      // function

HelloFunctionObject helloFunctionObject;
thread t2(helloFunctionObject);                // function object

thread t3([]{ cout << "lambda function"; });   // lambda function

The creator of a thread has to take care of the lifetime of its created thread. The executable unit of the created thread ends with the end of the callable. Either the creator is waiting until the created thread t is done (t.join()) or the creator detaches itself from the created thread: t.detach(). A thread t is joinable if no operation t.join() or t.detach() was performed on it. A joinable thread calls in its destructor the exception std::terminate, and the program terminates.

thread t1(helloFunction);                    // function

HelloFunctionObject helloFunctionObject;
thread t2(helloFunctionObject);              // function object

thread t3([]{ cout << "lambda function"; }); // lambda function

t1.join();
t2.join();
t3.join();

You can move a callable from one thread to another.

#include <thread>
...
std::thread t([]{ cout << "lambda function"; });  
std::thread t2;
t2= std::move(t);

std::thread t3([]{ cout << "lambda function"; });
t2= std::move(t3);                              // std::terminate

By performing t2= std::move(t) thread t2 has the callable of thread t. Assuming thread t2 already had a callable and is joinable the C++ runtime would call std::terminate. This happens exactly in t2= std::move(t3) because t2 neither executed t2.join() nor t2.detach() before.

std::thread is a variadic template.

void printStringCopy(string s){ cout << s; }
void printStringRef(const string& s){ cout << s; }

string s{"C++"};

thread tPerCopy([=]{ cout << s; });           // C++
thread tPerCopy2(printStringCopy, s);         // C++
tPerCopy.join();
tPerCopy2.join();

thread tPerReference([&]{ cout << s; });       // C++
thread tPerReference2(printStringRef, s);      // C++
tPerReference.join();
tPerReference2.join();
MethodDescription
t.join()Waits until thread t has finished its executable unit.
t.detach()Executes the created thread t independent of the creator.
t.joinable()Checks if thread t supports the calls join or detach.
t.get_id() andReturns the identity of the thread.
this_thread::get_id() 
thread::hardware_concurrency()Indicates the number of threads that can be run in parallel.
this_thread::sleep_until(absTime)Puts the thread t to sleep until time absTime.
this_thread::sleep_for(relTime)Puts the thread t to sleep for the duration relTime.
this_thread::yield()Offers the system to run another thread.
t.swap(t2) andSwaps the threads.
swap(t1, t2) 

You can only call t.join() or t.detach() once on a thread t. If you attempt to call these more than once you get the exception std::system_errorstd::thread::hardware_concurrency returns the number of cores or 0 if the runtime cannot determine the number. The sleep_until and sleep_for operations needs a time point or a time duration as argument.

Threads cannot be copied but can be moved. A swap operation performs a move when possible.

using std::this_thread::get_id;

std::thread::hardware_concurrency();      // 4

std::thread t1([]{ get_id(); });            // 139783038650112
std::thread t2([]{ get_id(); });            // 139783030257408
t1.get_id();                                // 139783038650112
t2.get_id();                                // 139783030257408

t1.swap(t2);

t1.get_id();                                // 139783030257408
t2.get_id();                                // 139783038650112
get_id();                                   // 140159896602432

Shared Variables

mutex

#include <mutex>
#include <thread>
...

using namespace std;

std::mutex mutexCout;

struct Worker{
  Worker(string n):name(n){};
  void operator() (){
    for (int i= 1; i <= 3; ++i){
      this_thread::sleep_for(chrono::milliseconds(200));
      mutexCout.lock();
      cout << name << ": " << "Work " << i << endl;
      mutexCout.unlock();
    }
  }
private:
  string name; 
};

thread herb= thread(Worker("Herb"));
thread andrei= thread(Worker(" Andrei"));
thread scott= thread(Worker ("    Scott"));
thread bjarne= thread(Worker("      Bjarne"));
Methodmutexrecursive_mutextimed_mutexrecursive_timed_mutexshared_timed_mutex
m.lockyesyesyesyesyes
m.unlockyesyesyesyesyes
m.try_lockyesyesyesyesyes
m.try_lock_for  yesyesyes
m.try_lock_until  yesyesyes

The std::shared_time_mutex enables it to implement reader-writer locks. The method m.try_lock_for(relTime) needs a relative time duration; the method m.try_lock_until(absTime) a absolute time point.

Deadlocks A deadlock is a state in which two or more threads are blocked because each thread waits for the release of a resource before it releases its resource.

You can get a deadlock very quickly if you forget to call m.unlock(). That happens for example in case of an exception in the function getVar().

m.lock();
sharedVar= getVar();//dont use lock inside getVar
m.unlock()

Locks You should encapsulate a mutex in a lock to release the mutex automatically. A lock is an implementation of the RAII idiom because the lock binds the lifetime of the mutex to its lifetime. C++11 has std::lock_guard for the simple and std::unique_lock for the advanced use case, respectively. Both need the header <mutex>. With C++14 C++ has a std::shared_lock which is in the combination with the mutex std::shared_time_mutex the base for reader-writer locks.

std::lock_guard supports only the simple use case. Therefore it can only bind its mutex in the constructor and release it in the destructor.

std::mutex coutMutex;

struct Worker{
  Worker(std::string n):name(n){};
  void operator() (){
    for (int i= 1; i <= 3; ++i){
      std::this_thread::sleep_for(std::chrono::milliseconds(200));
      std::lock_guard<std::mutex> myLock(coutMutex);
      std::cout << name << ": " << "Work " << i << std::endl;
    }
  }
private:
  std::string name;
};

unique_lock is a general-purpose mutex ownership wrapper allowing deferred locking, time-constrained attempts at locking, recursive locking, transfer of lock ownership, and use with condition variables.

#include <mutex>
#include <thread>
#include <chrono>
 
struct Box {
    explicit Box(int num) : num_things{num} {}
 
    int num_things;
    std::mutex m;
};
 
void transfer(Box &from, Box &to, int num)
{
    // don't actually take the locks yet
    std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
    std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
 
    // lock both unique_locks without deadlock
    std::lock(lock1, lock2);
 
    from.num_things -= num;
    to.num_things += num;
 
    // 'from.m' and 'to.m' mutexes unlocked in 'unique_lock' dtors
}
 
int main()
{
    Box acc1(100);
    Box acc2(50);
 
    std::thread t1(transfer, std::ref(acc1), std::ref(acc2), 10);
    std::thread t2(transfer, std::ref(acc2), std::ref(acc1), 5);
 
    t1.join();
    t2.join();
}

Thread Local Data

By using the keyword thread_local, you have thread local data also known as thread local storage. Each thread has its copy of the data. Thread-local data behaves like static variables. They are created at their first usage, and their lifetime is bound to the lifetime of the thread.

std::mutex coutMutex;
thread_local std::string s("hello from ");

void addThreadLocal(std::string const& s2){
  s+= s2;
  std::lock_guard<std::mutex> guard(coutMutex);
  std::cout << s << std::endl;
  std::cout << "&s: " << &s << std::endl;
  std::cout << std::endl;
}

std::thread t1(addThreadLocal, "t1");
std::thread t2(addThreadLocal, "t2");
std::thread t3(addThreadLocal, "t3");
std::thread t4(addThreadLocal, "t4");

Condition Variables

Condition variables enable threads to be synchronised via messages. They need the header <condition_variable>. One thread acts as a sender, and the other as a receiver of the message. The receiver waits for the notification of the sender.

MethodDescription
cv.notify_one()Notifies a waiting thread.
cv.notify_all()Notifies all waiting threads.
cv.wait(lock, ...)Waits for the notification while holding a std::unique_lock.
cv.wait_for(lock, relTime, ...)Waits for a time duration for the notification while holding a std::unique_lock.
cv.wait_until(lock, absTime, ...)Waits until a time for the notification while holding a std::unique_lock.

Sender and receiver need a lock. In case of the sender a std::lock_guard is sufficient, because it only once calls lock and unlock. In the case of the receiver a std::unique_lock is necessary, because it typically often locks and unlocks its mutex.

// conditionVariable.cpp 
...
#include <condition_variable>
...

std::mutex mutex_;
std::condition_variable condVar;
bool dataReady= false;

void doTheWork(){
  std::cout << "Processing shared data." << std::endl;
}

void waitingForWork(){
  std::cout << "Worker: Waiting for work." << std::endl;
  std::unique_lock<std::mutex> lck(mutex_);
  condVar.wait(lck, []{ return dataReady; });//wait the notify
  doTheWork();
  std::cout << "Work done." << std::endl;
}

void setDataReady(){
  std::lock_guard<std::mutex> lck(mutex_);
  dataReady=true;
  std::cout << "Sender: Data is ready." << std::endl;
  condVar.notify_one();
}

std::thread t1(waitingForWork);
std::thread t2(setDataReady);

Tasks

In addition to threads, C++ has tasks to perform work asynchronously. Tasks need the header <future>. A task is parameterised with a work package and consists of the two associated components, a promise and a future. Both are connected via a data channel. The promise executes the work packages and puts the result in the data channel; the associated future picks up the result. Both communication endpoints can run in separate threads. What’s special is that the future can pick up the result at a later time. Therefore the calculation of the result by the promise is independent of the query of the result by the associated future.

asks behave like data channels. The promise puts its result in the data channel. The future waits for it and picks it up.

Threads are very different from tasks.

For the communication between the creator thread and the created thread, you have to use a shared variable. The task communicates via its data channel, which is implicitly protected. Therefore a task must not use a protection mechanism like a mutex.

The creator thread is waiting for its child with the join call. The future fut is using the `fut.get() call which is blocking if no result is there.

If an exception happens in the created thread, the created thread terminates and therefore the creator and the whole process. On the contrary, the promise can send the exceptions to the future, which has to handle the exception.

A promise can serve one or many futures. It can send a value, an exception or only a notification. You can use a task as a safe replacement for a condition variable.

#include <future>
#include <thread>
...

int res;
std::thread t([&]{ res= 2000+11;});
t.join();
std::cout << res << std::endl;             // 2011

auto fut= std::async([]{ return 2000+11; });
std::cout << fut.get() << std::endl;       // 2011

std::async behaves like an asynchronous function call. This function call takes a callable and its arguments. std::async is a variadic template and can, therefore, take an arbitrary number of arguments. The call of std::async returns a future object fut. That’s your handle for getting the result via fut.get(). Optionally you can specify a start policy for std::async. You can explicitly determine with the start policy if the asynchronous call should be executed in the same thread (std::launch::deferred) or in another thread (std::launch::async).

What’s special about the call auto fut= std::async(std::launch::deferred, ... ) is that the promise will not immediately be executed. The call fut.get() lazy starts the promise.Lazy and eager with std::async

#include <future>
...
using std::chrono::duration;
using std::chrono::system_clock;
using std::launch;

auto begin= system_clock::now();

auto asyncLazy= std::async(launch::deferred, []{ return system_clock::now(); });
auto asyncEager= std::async(launch::async, []{ return system_clock::now(); });
std::this_thread::sleep_for(std::chrono::seconds(1));

auto lazyStart= asyncLazy.get() - begin;
auto eagerStart= asyncEager.get() - begin;

auto lazyDuration= duration<double>(lazyStart).count();
auto eagerDuration= duration<double>(eagerStart).count();

std::cout << lazyDuration << " sec";                 // 1.00018 sec.
std::cout << eagerDuration << " sec";                // 0.00015489 sec.

The output of the program shows that the promise associated with the future asyncLazy is executed one second later than the promise associated with the future asyncEager. One second is exactly the time duration the creator is sleeping before the future asyncLazy asks for its result.

std::packaged_task enables you to build a simple wrapper for a callable, which can later be executed on a separate thread.

Therefore four steps are necessary.

std::packaged_task<int(int, int)> sumTask([](int a, int b){ return a+b; });
std::future<int> sumResult= sumTask.get_future();
sumTask(2000, 11);  
sumResult.get();
...
#include <future>
...

using namespace std;

struct SumUp{
  int operator()(int beg, int end){
    for (int i= beg; i < end; ++i ) sum += i;
    return sum;
  }
private:
  int beg;
  int end;
  int sum{0};
};

SumUp sumUp1, sumUp2;

packaged_task<int(int, int)> sumTask1(sumUp1);
packaged_task<int(int, int)> sumTask2(sumUp2);

future<int> sum1= sumTask1.get_future();
future<int> sum2= sumTask2.get_future();

deque< packaged_task<int(int, int)>> allTasks;
allTasks.push_back(move(sumTask1));
allTasks.push_back(move(sumTask2));

int begin{1};
int increment{5000};
int end= begin + increment;

while (not allTasks.empty()){
  packaged_task<int(int, int)> myTask= move(allTasks.front());
  allTasks.pop_front();
  thread sumThread(move(myTask), begin, end);
  begin= end;
  end += increment;
  sumThread.detach();
}

auto sum= sum1.get() + sum2.get();
cout << sum;                                   // 50005000

The promises (std::packaged_task) are moved into the std::deque allTasks. The program iterates in the while loop through all promises. Each promise runs in its thread and performs its addition in the background (sumThread.detach()). The result is the sum of all numbers from 0 to 100000.

The pair std::promise and std::future give the full control over the task.

MethodDescription
prom.swap(prom2) andSwaps the promises.
std::swap(prom, prom2) 
prom.get_future()Returns the future.
prom.set_value(val)Sets the value.
prom.set_exception(ex)Sets the exception.
prom.set_value_at_thread_exit(val)Stores the value and makes it ready if the promise exits.
prom.set_exception_at_thread_exit(ex)Stores the exception and makes it ready if the promise exits.
MethodDescription
fut.share()Returns a std::shared_future.
fut.get()Returns the result which can be a value or an exception.
fut.valid()Checks if the result is available. Returns after the call fut.get() false.
fut.wait()Waits for the result.
fut.wait_for(relTime)Waits for a time duration for the result.
fut.wait_until(absTime)Waits until a time for the result.

If a future fut asks more than once for the result, a std::future_error exception is thrown. The future creates a shared future by the call fut.share(). Shared future are associated with their promise and can independently ask for the result. A shared future has the same interface as a future.

Here is the usage of promise and future.

#include <future>
...

void product(std::promise<int>&& intPromise, int a, int b){
  intPromise.set_value(a*b);
}

int a= 20;
int b= 10;

std::promise<int> prodPromise;
std::future<int> prodResult= prodPromise.get_future();

std::thread prodThread(product, std::move(prodPromise), a, b);
std::cout << "20*10= " << prodResult.get();//wait intPromise.set_value 20*10= 200

The promise prodPromise is moved into a separate thread and performs its calculation. The future gets the result by prodResult.get().

A future fut can be synchronised with is associated promise by the call fut.wait(). Contrary to condition variables, you need no locks and mutexes, and spurious and lost wakeups are not possible.

// promiseFutureSynchronise.cpp
...
#include <future>
...

void doTheWork(){
 std::cout << "Processing shared data." << std::endl;
}

void waitingForWork(std::future<void>&& fut){
 std::cout << "Worker: Waiting for work." <<
 std::endl;
 fut.wait();//wait for set_value
 doTheWork();
 std::cout << "Work done." << std::endl;
}

void setDataReady(std::promise<void>&& prom){
 std::cout << "Sender: Data is ready." <<
 std::endl;
 prom.set_value();
}

std::promise<void> sendReady;
auto fut= sendReady.get_future();

std::thread t1(waitingForWork, std::move(fut));
std::thread t2(setDataReady, std::move(sendReady));

The call of the promise prom.set_value() wakes up the future which then can perform its work

Qt Internationalization

Most real projects have a target audience in multiple countries. The most notable difference between them is the spoken language, but there are other aspects some developers may not think of. For example, dot "." and comma "," are both fairly common as the decimal separator throughout the world. Date formats are also very different and incompatible, and using a wrong format (for example, mm/dd/yyyy instead of dd/mm/yyyy) will result in a completely different date.

Qt provides the QLocale class for dealing with locale-dependent operations, including conversions between numbers in strings. In the following code, text and number may have different values, depending on the system locale:

QLocale locale = QLocale::system();
QString text = locale.toString(1.2);
double number = locale.toDouble(QStringLiteral("1,2"));

QLocale also provides methods for formatting dates and prices, and allows us to request additional information about local conventions.

Translator

ts files

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en" sourcelanguage="en">
<context>
    <name>QApplication</name>
    <message>
        <source>Hello2</source>
        <translatorcomment>there are no comment</translatorcomment>
        <translation>iam good</translation>
    </message>
</context>
<context>
    <name>QLabel</name>
    <message>
        <source>Hello</source>
        <translatorcomment>there are no comment</translatorcomment>
        <translation>iam good</translation>
    </message>
</context>
</TS>

in Qt Linguist you can release ts file to qm binary file to load by QTranslator

QTranslator translator;
if(translator.load("PNP_en_US","..\\PNP\\")){
   qDebug()<<"load translation file";
}
a.installTranslator(&translator);
qDebug()<<a.tr("Hello2");
qDebug()<<QLabel::tr("Hello");