【问题标题】:Best (cleanest) way for writing platform specific code编写平台特定代码的最佳(最干净)方式
【发布时间】:2015-09-20 20:47:52
【问题描述】:

假设您有一段代码必须根据程序运行的操作系统而有所不同。
这是老派的做法:

#ifdef WIN32
   // code for Windows systems
#else
   // code for other systems
#endif

但必须有比这个更清洁的解决方案,对吧?

【问题讨论】:

  • 我讨厌条件编译。恨恨恨。我更喜欢通用头文件和单独的实现文件。更干净,更容易理解,并且通常最少的代码重复。例如,oswrapper.h、oscommon.cpp、osposix.cpp、oswindows.cpp 并找出 osposix 和 oswindows 中的哪一个在 makefile 或 IDE 的 target/profile/whatever 中构建和链接。
  • 我必须支持上述帖子。我很少会使用ifdef 指令,那就是如果许多操作系统中的一个有一个特殊的怪癖,任何查看代码和阅读简短评论的人都可以快速理解和修复。在这些情况下,将所有内容分成多个文件的认知开销远远大于ifdef 和评论。
  • @BaummitAugen 手头有硬件支持、驱动程序等的此类库吗?这不是一个很有帮助的一般性陈述。
  • @Dogbert 如果该评论应该是答案,我会将其发布为答案。但事实并非如此,使用现有代码而不是例如使用现有代码是一个简单的建议。在 Windows 上编写另一个与__int64 混淆的标头。
  • @BaummitAugen 够公平的。

标签: c++ qt


【解决方案1】:

在我的职业生涯中,我在六家公司亲眼看到的典型方法是使用硬件抽象层 (HAL)。

这个想法是将最低级别的东西放入一个专用的标头和静态链接库中,其中包括以下内容:

  • 固定宽度整数(Linux 上为int64_t,Windows 上为__int64,等等)。
  • 通用库函数(strtok_r() vs strtok_s() 在 Linux 和 Windows 上)。
  • 通用数据类型设置(即:所有数据类型的 typedefs,例如 xInt、xFloat 等,在整个代码中使用,以便在平台的底层类型发生更改或突然支持新平台时,无需重新编写和重新测试依赖它的代码,这在劳动力方面可能非常昂贵)。

HAL 本身通常充斥着您的示例中的预处理器指令,这就是事实。如果你用运行时if/else 语句包装它,你的编译将由于未解析的符号而失败。或者更糟的是,您可能会包含额外的符号,这会增加输出的大小,并且如果频繁执行该代码可能会减慢您的程序。

只要 HAL 编写得很好,HAL 的标头和库就会为您提供一个通用接口和一组数据类型,以便在您的其余代码中轻松使用。

从专业的角度来看,这其中最美妙的方面是,所有您的其他代码都不必关心架构或操作系统的细节。您将在各种系统上拥有相同的代码流,通过扩展,您可以以各种不同的方式测试相同的代码,并找到您通常不会期望或测试的错误。从公司的角度来看,这在劳动力方面节省了大量资金,并且不会因为客户对生产软件中的错误感到愤怒而失去客户。

【讨论】:

  • 您的回答很好,但在我看来,并非所有平台缓解代码都可以称为“HAL”。例如,在处理与硬件无关的操作系统差异时,例如Sleep(DWORD milliseconds) 和sleep(uint seconds)。如果我错了,请告诉我。
  • @Marc.2377 对于类似的事情,您只需使用自己的 sleep() 函数将它们包装起来,并使用一个共同的时间基准。即:xsleep(xUint64_t nanoseconds)。您的所有代码都使用此功能,并且您的平台特定构建的 HAL 翻译它:) 唯一出现问题的情况是,如果您尝试使用截然不同的系统(即:具有不同的线程、安全性、网络模型等)。在这种情况下,编写 HAL 变得更加困难。
  • 确实如此,我曾经就是这样做的。然而,我的评论是专门关于使用“HAL”术语来指代与硬件完全无关的抽象层。
  • @Marc.2377 它有时被称为“PAL”,但“HAL”是一个有点笼统的术语,被广泛使用,通常表示操作系统和架构抽象。
【解决方案2】:

在我的职业生涯中,我不得不做很多这样的事情,支持在嵌入式设备上构建和运行的代码,以及在 Windows 中,然后让它在不同的 ASICS 和/或 ASICS 修订版上运行.

我倾向于按照你的建议去做,然后当事情真的发生分歧时,继续定义我希望在平台之间修复的接口,然后拥有单独的实现文件甚至库。随着代码库变老并且需要添加更多异常,它会变得非常混乱。

有时您可以将这些内容隐藏在头文件中,因此您的代码看起来很“干净”,但很多时候这只是混淆了一堆宏魔法背后发生的事情。

我要添加的唯一另一件事是,如果没有定义任何选项,我倾向于使#ifdef/#else/#endif 链失败。这迫使我在新版本出现时重新审视这个问题。有些人更喜欢它有一个默认值,但我发现这只是隐藏了潜在的失败。

当然,我在嵌入式世界中工作,其中代码空间至关重要(因为内存很小且固定),不幸的是,代码清洁度不得不退居二线。

【讨论】:

    【解决方案3】:

    对于不平凡的项目,一种采用的做法是将特定于平台的代码编写在单独的文件中(如果适用,也可以在单独的目录中),尽可能避免“本地化”#ifdefs。

    假设您正在开发一个名为“Example”的库,example.hpp 将是您的库头:

    example.hpp

    #include "platform.hpp"
    
    //
    // here: platform-independent declarations, includes etc
    //
    
    
    // below: platform-specific includes    
    
    #if defined(WINDOWS)
    
    #include "windows\win32_specific_code.hpp"
    // other win32 headers
    
    #elif defined(POSIX)
    #include "posix/linux_specific_code.hpp"
    // other linux headers
    
    #endif
    

    platform.hpp(简体)

    #if defined(WIN32) && !defined(UNIX)
    #define WINDOWS
    #elif defined(UNIX) && !defined(WIN32)
    #define POSIX
    #endif
    

    win32_specific_code.hpp

    void Function1();
    

    win32_specific_code.cpp

    #include "../platform.hpp"
    
    #ifdef WINDOWS  // We should not violate the One Definition Rule
    #include "win32_specific_code.hpp"
    #include <iostream>
    
    void Function1()
    {
        std::cout << "You are on WINDOWS" << std::endl;
    }
    
    //...
    
    #endif /* WINDOWS */
    

    当然,在您的 linux_specific_code.hpp 文件中声明 Function1()。

    然后,在为 Linux 实现它时(在 linux_specific_code.cpp 文件中),请务必将所有内容都包含在条件编译中,类似于我上面所做的(例如,使用 #ifdef POSIX)。否则,编译器将生成多个定义,您将收到链接器错误。

    现在,您库的用户必须做的所有事情都是在他的代码中使用#include &lt;example.hpp&gt;,并将#define WINDOWS 或#define POSIX 放在他的编译器的预处理器定义中。事实上,第二步可能根本不需要,假设他的环境已经定义了WIN32 或UNIX 宏之一。这样一来,Function1() 就可以从代码中跨平台使用了。


    这种方法几乎是Boost C++ Libraries 使用的方法。我个人觉得它干净而明智。但是,如果您不喜欢它,可以在 Chromium's conventions for multi-platform development 上阅读,了解一些不同的策略。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-15
      • 1970-01-01
      • 2020-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多