如何在多平台qt代码中避免特定的#ifdef?

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

我有一个 QT 输入侦听器 class,它在 运行 QCoreApplication 中发出 stdin 输入信号。我想在 windows 和 linux.

上都使用它

我目前的做法是在header和cpp中使用#ifdef Q_OS_WIN来执行platform-specific代码。据我所知,#ifdef 被认为是有害的,应该避免,我想以一种我有一个 header 文件 inputlistener.h 的方式重构它,让构建系统在一个具体的 windows/inputlistener.cpplinux/inputlistener.cpp,可能还有一个额外的 inputlistener_global.cpp 来保存代码,这不是平台特定的。

但是,我找不到解决办法,如何让 header 中的 #ifdef 不受影响。

我怎样才能做到这一点?

这是我目前的做法:

#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);
    }
}

您可以创建 WinEventListenerUnixEventListener(或其他),每个都使用它自己的实现(而不是尝试通过 ifdefs 将其放入一个),每个都实现通用的 Listener接口(并驻留在单独的文件中)。

然后,有一个 return 监听器适合 OS 的工厂函数。他们只有一个地方可能需要 ifdefs。

但总的来说,ifdef做一些事情可能是最好的或唯一的行动方案(例如,当你已经抽象了一些东西时)。条件编译是预处理器的少数 valid/justified 用法之一(这就是它的用途)。

此外,在您的特定情况下,请确保 Qt 库中还没有合适的 code/class。对于大多数常见的东西,可能已经存在抽象(或推荐的方法)。

您可以为 windowsunix 创建单独的 EventListener.cpp 文件,并将这些文件放入子目录中,例如 (winlinux)。您可以向 makefile 或 projectfile 添加一个基于当前平台的实现文件。编译器将只为当前平台编译一个文件。

使用此方法可以完全避免 ifdefing。

如果定义不同你可以使用pImpl成语来分隔class的实现细节:https://cpppatterns.com/patterns/pimpl.html