编写平台特定代码的最佳(最干净)方式

时间:2015-09-20 20:47:53

标签: c++ qt

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

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

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

3 个答案:

答案 0 :(得分:2)

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

我们的想法是,您将最低级别的内容放入专用标头和静态链接库中,其中包括以下内容:

  • 固定宽度整数(Linux上为int64_t,Windows上为__int64等)。
  • Linux和Windows上的公共库函数(strtok_r() vs strtok_s())。
  • 常见的数据类型设置(即:所有数据类型的typedef,例如xIntxFloat等,在整个代码中使用,以便基础类型更改为平台或新的平台突然得到支持,无需重新编写和重新测试依赖于它的代码,这在人工方面可能非常昂贵。)

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

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

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

答案 1 :(得分:0)

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

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

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

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

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

答案 2 :(得分:0)

非平凡项目采用的做法是在单独的文件中(在适用的单独目录中)编写特定于平台的代码,尽可能避免“本地化”#ifdef

假设您正在开发一个名为“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 */

当然,也可以在Function1()文件中声明linux_specific_code.hpp

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

现在,您的库的用户必须在其代码中执行#include <example.hpp>,并将#define WINDOWS#define POSIX放入其编译器的预处理器定义中。事实上,假设他的环境已经定义了WIN32UNIX宏中的任何一个,那么第二步可能根本就没有必要。这样,Function1()已经可以以跨平台的方式从代码中使用。

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