Cross Qt Development!

It’s been ten months since Qt 5 has been released. The first official version was a disastrous one! 5.0.1 was a little bit better, 5.1.0 was good enough, and now we have 5.1.1 that is a great one.  As a Qt developer it’s not so hard to get used to the new version. There is a comprehensive guide here  to help developers to port their codebase from 4th to 5th Qt.

The same thing is now happening to me now. Sooner or later, I will have to port my applications to Qt5. So what’s I’m waiting for? Oh no… Backward Compatibility of The Source in Code! This makes things a little bit hard for me. Because I make and deploy my applications on Windows using latest version of Qt, but same products rely on operating system version of the Qt libraries in Linux machines. Most distributions still keep Qt4 in their repositories as main version of Qt. Obviously because of huge list of stuff that depend on 4th Qt.

So it looks best solution for me is porting the code base into a median state that the code can be compiled with both Qt 4 and 5 libraries. That sounds pretty boring. Actually it is.

I will use same approach with my previous post (Cross Platform Development). So the first rule is:

I. If there exists a way (W) to do task (T) that is compatible with both 4th and 5th Qt, use it, unless it’s  more complicated than collective complexity of Qt4-specific code (W4) and  Qt5-specific code (W5). Mathematically speaking,

if( W < W4+ W5) {
     T.do(W);
} 
else if (W > W4 + W5) {
     if(Qt5)
         T.do(W5);
     else
         T.do(W4);
}

Rule number two is obvious result of rule number one.

If there is no common code between Qt4 and 5, write both codes and choose which one to compile at compile time.

Let me show an example from Qtz project. In project files I have to take care of changes in module system. Fortunately there exist qmake macros that detect Qt version. More than this, there exist conditional directives that can be used for detecting Qt version.

QT += core gui sql
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
greaterThan(QT_MAJOR_VERSION, 4): CONFIG   += C++11
lessThan(QT_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -std=c++0x

Same rule applies in source code:

CandleGraphicsItem::CandleGraphicsItem(const Candle& candle, QGraphicsItem *parent) :
    QGraphicsItem(parent), candle_(candle)
{
#if QT_VERSION >= 0x050000
    setAcceptHoverEvents(true);
#else
    setAcceptsHoverEvents(true);
#endif
    this->setY(-(candle.low_+candle.high_)/2.0);
}

Using QT_VERSION macro from <QtGlobal> header, you can detect which Qt version is used to compile your code. That’s cool 🙂 Now I can compile same code with every version of Qt since 4.6 to 5.1.1.

For a detailed description on Qt4 and Qt5 read this article by KDAB guys. They have also developed an automated script to convert codes from 4th to 5th Qt.

Cross-platform Developmen

I would like my programs and libraries to work on different platforms, as much as possible. Primary target is always Linux and Unix-like systems of course, though I would like to provide same functionality on Windows and maybe Android and iOS. The platform, should not be a restriction for using softwares. This lead to the idea of Cross-Platform Development. Unfortunately it’s not easy. There is no accepted standardization of operation system APIs or common operations between operating systems. Developers should write code with cross-platform requirements in mind.  I’m going to describe my own experience with cross-platform development in the Artificial Intelligence Toolkit project. I encountered lots of problems and resolved some of them. This article provides a simple check-list to help developers avoid most frequent issues of cross-platform development in C/C++. Let’s go ahead 🙂

The Language

I. So shalt not code in a platform-specific language.

Just don’ do that. Don’t code in C#, C++ CLI or other Microsoft tools.

C++ is cross-platform enough

C++ is cross-platform enough cause it has a compiler on every platform (at least major ones)

The Code

II. So shalt not write platform-dependent code. Even in cost of adding more dependencies.

Rule number one says that your code should not rely on platform-specific staff. That’s not using Berkeley socket API in Linux or its Windows compeer, Winsock for example. Rather than using such APIs, add a third party dependency. For above example, using ASIO to do networking may be a cross-platform solution. ASIO itself is cross-platform so your code will run on both Windows and Linux.

In the case that you are developing an operating system level API, then you have to write code for each platform and specify which code to compile using compile-time conditional directives.

void delay(int ms)
{
#ifdefined(OS_WIN32)
              Sleep(ms);
#elifdefined(OS_LINUX)
             usleep(ms*1000);
#endif
}

In the presence of standard mechanism for any task, developer should consider about using standard way rather than invoking operating system functions. Above code can be replaced with this one:

void delay(int ms)
{
    // Guaranteed to run properly on every platform if it compiles!
    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}

Second delay need C++11 support. After 2014 guys at Microsoft may decide their compiler suite not to be an outdated one and may support C++11 completely.

The Build System

III. So shalt not use a platform-specific build system. Even in cost of providing more than one build system for different platforms.

Different platforms use different build systems. Most common build systems are GNU make and Microsoft’s NMake. There are plenty of nice build script generation tools like GNU autohell, CMake, Qt’s qmake and Microsoft Visual Studo! Yes that’s true 🙂 Visual Studio is an IDE that generates makefile from solution file. So it can be considered as a build script generator.

Personally I prefer autotools for both Linux and Windows. On windows it can be used with MSYS/MinGW with no problem. After all, from compiler to build system, the GNU toolchain can not be considered as a first degree citizen in Windows. That’s why you may want to compile your code with MSVC. Fortunately build systems do not conflict with each other. Simply add a visual studio project to source sandbox at the same level with your autotools files. If you feel so scrupulous, you can jail build system files in different directories and share source code between them. Such a development ecosystem will happily continue to its life with no trouble.