darkmattercoder
darkmattercoder

Reputation: 312

How to avoid specific #ifdef in multi - platform qt code?

I have a QT input listener class, that signals stdin inputs in a running QCoreApplication. I want to use that on both windows and linux.

My current approach is to use #ifdef Q_OS_WIN inside both header and cpp to execute the platform-specific code. As I know, that #ifdef is considered harmful and should be avoided, I want to refactor this in a manner where I have one single header file inputlistener.h and let the build system choose between a specific windows/inputlistener.cpp or linux/inputlistener.cpp, maybe with an additional inputlistener_global.cpp that holds the code, which is not platform specific.

However, I can't find a solution, how to get the #ifdef in the header out of the way.

How can I achieve that?

Here is my current approach:

#inputlistener.h

#ifndef INPUTLISTENER_H
#define INPUTLISTENER_H

#include <QtCore>

class inputlistener : public QObject {
    Q_OBJECT

private:
#ifdef Q_OS_WIN
    QWinEventNotifier* m_notifier;
#else
    QSocketNotifier* m_notifier;
#endif

signals:

    void inputeventhappened(int keycode);

private slots:

    void readyRead();

public:
    inputlistener();
};

#endif // INPUTLISTENER_H

#inputlistener.cpp

#include "inputlistener.h"
#include "curses.h"

#ifdef Q_OS_WIN
#include <windows.h>
#endif

inputlistener::inputlistener()
{
#ifdef Q_OS_WIN
    m_notifier = new QWinEventNotifier(GetStdHandle(STD_INPUT_HANDLE));
    connect(m_notifier, &QWinEventNotifier::activated
#else
    m_notifier = new QSocketNotifier(0, QSocketNotifier::Read, this);
    connect(m_notifier, &QSocketNotifier::activated
#endif
        ,
        this, &inputlistener::readyRead);

    readyRead(); // data might be already available without notification
}

void inputlistener::readyRead()
{
    // It's OK to call this with no data available to be read.
    int c;
    while ((c = getch()) != ERR) {
        emit inputeventhappened(c);
    }
}

Upvotes: 2

Views: 1644

Answers (2)

Peter
Peter

Reputation: 1611

You can create separate EventListener.cpp files for windows and unix and put these files into subdirectories like (win, linux). To the makefile or to the projectfile you can add one implementation file based on the current platform. The compiler will compile just one file for the current platform.

With this method you can avoid ifdefing totally.

If the definitions are different you can use pImpl idiom to separate the implementation details of a class: https://cpppatterns.com/patterns/pimpl.html

Upvotes: 2

Dan M.
Dan M.

Reputation: 4052

You can create WinEventListener and UnixEventListener (or something else) each using it's own implementation (instead of trying to fit it into one via ifdefs), each implementing common Listener interface (and residing in separate files).

Then, have a factory function that'd return listener appropriate to OS. That they there would be only one single place that might require ifdefs.

But in general, ifdefing something might be the best or the only course of action (e.g. when you already abstracting something). Conditional compilation is one of the few valid/justified usages of preprocessor (it's what it was made for).

Also, in your particular case, be sure there is not already appropriate code/class in Qt lib. For most common stuff chances are there is already existing abstraction (or recommended ways to do that).

Upvotes: 1

Related Questions