Qt leads to better code.

Qt Eases Developers' Frustrations

By ICS Development Team

Qt developers are a passionate bunch. They love to talk about new features, new versions. How cool is this and what needs to be done with that. And it all makes sense. New features and new versions are, eh, new and exciting. 

The essential part of Qt though is taken for granted. Most of the time, it does not get the recognition it rightfully deserves. (Here’s a list of what is considered essential, and what are considered add-ons.) 

I was working on an interesting project lately. A cross-platform, server side application that can receive messages, create events and redistribute them further according to some rules. There’s no UI, but the application will include a notification mechanism to send information to outside entities. 

The project looked pretty straightforward. Well, until I noticed another requirement: no Qt. Oops! 

Well, I said to myself, rules are rules. The job has to be done and tasks have to be completed in time. So I set about checking them off one by one. To give you an idea of how this went, let’s start from the beginning — command line parsing and event loop. These are pretty basic tasks. Qt would suggest using QCommandLineParser and QCoreApplication. But without Qt, I had to parse command line parameters manually, and to use this construction to enable the event loop:

while(1) {
   std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

Not great, but not that bad either — especially given the fact that I managed to reuse command line parsing code from another project. 

Next, I had to implement an internal event loop and state machine to respond to the incoming events from various components of the system. I was not even thinking about the QStateMachine equivalent. It would be okay just using a timer that would call processMyEventsQueue() periodically. 

But guess what… there’s no such thing in the standard C++ library. So here’s what I ended up with:

class Machine::EventQueue
{
public:
    EventQueue() = default;
    ~EventQueue()
    {
        while (!m_events.empty()) {
            delete m_events.front();
            m_events.pop();
        }
    }

    void post(Event *event)
    {
        if (event) {
#ifdef VERBOSE_BUILD
            std::cout << "Posting Event: " << event->type() << std::endl;
#endif
            std::unique_lock<std::mutex> lock(m_mutex);
            m_events.push(event);
            m_waitCondition.notify_one();
        }
    }

    Event *popEvent()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        while (m_events.empty()) {
#ifdef VERBOSE_BUILD
            std::cout << "Waiting for events" << std::endl;
#endif
            m_waitCondition.wait(lock);
        }
        auto event = m_events.front();
#ifdef VERBOSE_BUILD
        if (event)
            std::cout << "Got Event: " << event->type() << std::endl;
#endif
        m_events.pop();
        return event;
    }

private:
    std::mutex m_mutex;
    std::condition_variable m_waitCondition;
    std::queue<Event *> m_events;
};
...
void Machine::start()
{
    m_worker = new std::thread([this]() -> void {
        while (true) {
            auto event = m_eventQueue->popEvent();
            if (event) {
                // Process the event
                ...
                delete event;
            }
        }
    });
}

Next thing I needed was a piece of code to check whether std::list container contains an element.

template <typename T>
bool contains(std::list<T>& listOfElements, const T& element)
{
// Find the iterator if element in list
auto it = std::find(listOfElements.begin(), listOfElements.end(), element);
return it != listOfElements.end();
}

I would rather have used bool QList::contains(const T &value) const. If I could, I would not even be talking about signal slot communication — especially in a multi-threaded environment. But sadly, it was not an option. One fully appreciates how great the signals and slots mechanism is only when one can’t use it!  

Qt Leads to Better Code

I am certain that using Qt  would result in better, more elegant code. It would be faster to develop and easier to maintain. There are well-designed APIs, a mature code base, and excellent documentation. This combination is pretty hard to beat.

Yes, using Qt would add dependency to a project — but it is dependency on a toolkit where all of the components use the same design philosophy and work together as a single entity. For my project I would need only two modules: QtCore and Qt Network. Qt Core alone would remove 90% of my pain points. 

(There is an argument to be made that standard C++ libraries are more efficient than Qt. This may be so, but for this application, which does not have any real time requirements, it hardly matters. Processing power is cheap these days, but engineering time is not. Besides, adding dependencies in terms of external packages may completely negate all the efficiency gained by using standard C++ components.) 

So, if Qt is so great — and would lead to better code on a faster timeline — why did this project include a no-Qt requirement? The answer is simple: cost. Developing this application would only require a fraction of what Qt offers. For instance, there was no need for widgets, QML or mobile support. (If I may offer an analogy: I needed a bag of potatoes and a pound of apples for this project, not the entire grocery store.) For that reason, a business decision was made to save costs associated with Qt licensing fees. Though this may make sense from a business perspective, as a developer all I can say is I really miss Qt! 

Interested in Qt? Learn how to build Qt 6.2.0 from source on Ubuntu Linux.