Posted inInformation Technology

C++ 11 and Qt — Part 1

The medical device touch screen world tends to stay well behind on “new” standards and features and for good reason. Given the 5+ year FDA mandated testing and approval cycle for things with varying potential of catastrophic patient outcome bleeding edge isn’t a welcome thing. Judging from recent contract postings some devices are locked into Qt 3.3.x because that is what was used when they were first built. Until recently most newer devices were sticking with Qt 4.8.x because it was the most stable at the time.

With Qt 6 on the not so distant horizon, a goodly number of new projects will begin with Qt 5.4.x or 5.6.x. With the advent of this reality we must all begin changing our coding standards, not just because of Qt changes, but because Qt has helped change the C++ standard and now offers support for c++11. I say Qt has changed the C++ standard because many of the “new” features found in C++ 11 appear to be directly lifted from Qt.

Before we delve into such things, we must first make certain the development environment supports C++ 11. I’m running 64-bit Ubuntu 16.04.

roland@roland-HP-Compaq-8100-Elite-SFF-PC:~$ which g++
/usr/bin/g++
roland@roland-HP-Compaq-8100-Elite-SFF-PC:~$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

roland@roland-HP-Compaq-8100-Elite-SFF-PC:~$ mkdir fopa
roland@roland-HP-Compaq-8100-Elite-SFF-PC:~$ cd fopa
roland@roland-HP-Compaq-8100-Elite-SFF-PC:~/fopa$ jed test1.cpp
roland@roland-HP-Compaq-8100-Elite-SFF-PC:~/fopa$ g++ -Wall -g -std=c++11 test1.cpp -o test1
roland@roland-HP-Compaq-8100-Elite-SFF-PC:~/fopa$ ./test1
2
3
5
roland@roland-HP-Compaq-8100-Elite-SFF-PC:~/fopa$ cat test1.cpp
#include <array>
#include <iostream>

int main()
{
 
 std::array<int, 3> arr{ 2, 3, 5 };
 
 for( auto i : arr)
 {
 std::cout << i << std::endl;
 }
 
}

Our simple program tested a few C++ 11 features. First is the initializer list, which is awesome. You can now initialize arrays outside of the code body of a constructor. More importantly, {} initialization refuses to narrow.

vector<int> vi = { 1, 2.3, 4, 5.6};

The above line has a pair of “obvious” typos. Under previous standards your program would compile with bugs. Under C++ 11 this narrowing from a floating point type to an int is an error caught at compile time saving you hours of debugging.

Next we tested the “auto” type which tells the compiler to figure out what it needs. While you may believe this is only a crutch for lazy programmers it is a maintenance boon. If you are long in the tooth like myself, you’ve had to do more than your fair share of maintenance programming. Think back to how many times a variable had to be widened. How many times have you had to search for every place a float was referenced because an application now needed a double, or an int now needed to be a qlonglong?

These weren’t initial design flaws. In the embedded world you still try to use the smallest data type which meets design requirements. How many times did you have a device which originally shipped with under 512Meg of RAM only to have the new generation come with 1Gig and a new requirement to run N days longer on battery? Given initial storage and processor limitations a 32-bit int had oceans more capacity than needed, but the new version now has 5-10 day longer constant run time, more RAM and a dramatically faster processor necessitating a 64-bit int. If you had used the auto data type you would only have to change one variable declaration, compile and regression test. Sadly, you now have to search for every place the variable was used.

Did you notice the syntax of the for loop? This is the new range-based for loop. As a Qt programmer you may notice the similarities to Qt’s foreach() loop. There appears to be a significant difference though. Unless you work in a resource constrained environment you probably do not know that foreach() makes a local copy of the container. Speed wise this can be expensive, resource wise it can be devastating. If you are developing for a desktop using a container which has 100 integers or less, you probably don’t care.

Besides working with values via the “auto” data type, the range-based for loop can work with references.

QStringList lst { "fred", "ethyl", "mayble", "George" };
for( const QString & str : lst)
{
    std::cout << qPrintable(str) << std::endl;
}

That said, if your container has large or complex objects which require a deep copy, your processor is slow to save battery and your RAM is tiny, using foreach() comes at too high of a price.

Foreach() performed a copy to enable dangerous programming. There was predictable behavior if you added or removed items while iterating. The new range-based for loop has undefined behavior when adding and removing items from a container while iterating.

Qt containers use implicit sharing so, in many cases a copy is cheap. The range-based for loop does not copy, it uses the begin() and end() iterator functions. Those functions will force a copy of a Qt container if it is shared and non const. It is recommended you pass const containers to avoid the price of a copy.