编写平台特定代码的最佳(最干净)方式
Best (cleanest) way for writing platform specific code
假设你有一段代码,它必须根据你的程序运行的操作系统而不同。
这是老派的做法:
#ifdef WIN32
// code for Windows systems
#else
// code for other systems
#endif
但是肯定有比这个更干净的解,对吧?
在我的职业生涯中,我在六家公司中亲眼看到的典型方法是使用硬件抽象层(HAL)。
这个想法是你把最低级别的东西放在一个专用的头文件和静态链接库中,其中包括如下内容:
- 固定宽度整数(Linux上的
int64_t
, Windows上的__int64
等)。 - 常用库函数(
strtok_r()
vsstrtok_s()
Linux vs Windows). - 一个通用的数据类型设置(即:所有数据类型的typedefs,如
xInt
,xFloat
等,在整个代码中使用,这样如果一个平台的底层类型改变,或者一个新平台突然被支持,不需要重写和重新测试依赖于它的代码,这在劳动力方面可能是非常昂贵的)。
HAL本身通常充满了预处理器指令,就像你的例子一样,这只是现实问题。如果用运行时if
/else
语句包装它,则由于无法解析的符号,编译将失败。或者更糟的是,您可能包含额外的符号,这会增加输出的大小,并且如果频繁执行该代码,可能会减慢程序的速度。
只要HAL编写得很好,HAL的头文件和库就会为您提供一个公共接口和一组数据类型,以便在您的其余代码中使用,从而减少麻烦。
从专业的角度来看,最美妙的方面是所有其他代码的都不必关注架构或操作系统的细节。您将在不同的系统上拥有相同的代码流,这将允许您以各种不同的方式测试相同的代码,并发现您通常不会期望或测试的错误。从公司的角度来看,这节省了大量的劳动力成本,并且不会因为客户对产品软件中的错误感到愤怒而失去客户。在我的职业生涯中,我不得不做很多这样的事情,支持在嵌入式设备和windows上构建和运行的代码,然后还让它在不同的ASICS和/或ASICS的修订版上运行。
我倾向于做你建议的,然后当事情真的发生分歧时,继续定义我希望在平台之间固定的接口,然后有单独的实现文件甚至库。当代码库变旧,需要添加更多异常时,它会变得非常混乱。
有时你可以把这些东西隐藏在头文件中,这样你的代码看起来"干净",但很多时候,这只是混淆了一堆宏魔术背后发生的事情。
我要添加的唯一一件事是,如果没有定义任何选项,我倾向于使#ifdef/#else/#endif链失败。这迫使我在新版本出现时重新审视这个问题。有些人喜欢它有一个默认值,但我发现这只是隐藏潜在的失败。
当然,我在嵌入式世界中工作,代码空间是至关重要的(因为内存很小且固定),不幸的是,代码清洁度不得不退居次要位置。
对于重要的项目,采用的做法是在单独的文件中编写特定于平平台的代码(在适用的情况下,在单独的目录中),尽可能避免"本地化"#ifdef
。
假设你正在开发一个名为"Example"的库,example.hpp
将是你的库头:
example.hpp
#include "platform.hpp"
//
// here: platform-independent declarations, includes etc
//
// below: platform-specific includes
#if defined(WINDOWS)
#include "windowswin32_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 <example.hpp>
,并将#define WINDOWS
或#define POSIX
放在编译器的预处理器定义中。实际上,假设他的环境已经定义了WIN32
或UNIX
宏中的一个,那么第二步可能根本没有必要。这样,Function1()
就可以在代码中以跨平台的方式使用了。
这种方法与Boost c++库中使用的方法非常相似。我个人觉得它既干净又明智。但是,如果您不喜欢它,您可以阅读Chromium的多平台开发惯例,以获得稍微不同的策略。
- 在c代码之间共享数据的最佳方式
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 从嵌套在std::映射中的std::列表中删除元素的最佳方式
- 如果条件为TRUE(最佳方式?),则在do while循环中后置增量
- 在reactor中存储eventHandlers的最佳方式是什么
- 在AVX通道中混洗的最佳方式
- 从 T 创建 std::future 的最佳方式<T>
- C++:使用 std::unique_ptr 访问重载运算符++的最佳方式?
- 对列表列表中的元素进行分组的最佳方式
- 利用 GPU 的最佳方式
- 使用 QT C++过滤大数据的最佳方式
- 算法设计:用边界数字表示 2D 网格的最佳方式,以C++?
- 在C++中共享键值对的最佳方式
- 为Catch2中的外部文本文件指定路径的最佳方式
- 代表Quarto棋盘游戏棋子的最佳方式
- 等待线程的最佳方式是什么
- 将uint8_t*buffer和size_tbufferlen从C++传递到C中的API函数的最佳方式是什么
- 创建控制台菜单C++的最佳方式
- 只显示片段着色器的最佳方式是什么
- 复制文件的最佳方式是什么,以便我可以在复制过程中轻松取消复制?