C ++:平台相关types – 最佳模式

我正在寻找一种模式来组织C ++中多个平台的头文件。

我有一个应该在Linux和Win32下编译的封装.h文件。

这是我能做的最好的吗?

// defs.h #if defined(WIN32) #include <win32/defs.h> #elif defined(LINUX) #include <linux/defs.h> #else #error "Unable to determine OS or current OS is not supported!" #endif // Common stuff below here... 

我真的不喜欢C ++中的预处理器的东西。 有没有一个干净(和理智)的方式来做得更好?

您应该使用能够执行平台检查的配置脚本,并生成相应的编译器标志和/或配置头文件。

有几种工具可以执行此任务,例如autotools , Scons或Cmake 。

在你的情况下,我会建议使用CMake,因为它很好地与Windows集成,能够生成Visual Studio项目文件以及Mingw makefiles。

这些工具背后的主要理念是,不要再次测试操作系统本身,而是针对可能存在或可能不存在的功能,或者哪些值可能会有所不同,从而降低代码无法编译的风险“错误。

这里是一个注释的CMake示例(CMakeFiles.txt):

 # platform tests include(CheckFunctionExists) include(CheckIncludeFile) include(CheckTypeSize) include(TestBigEndian) check_include_file(sys/types.h HAVE_SYS_TYPES_H) check_include_file(stdint.h HAVE_STDINT_H) check_include_file(stddef.h HAVE_STDDEF_H) check_include_file(inttypes.h HAVE_INTTYPES_H) check_type_size("double" SIZEOF_DOUBLE) check_type_size("float" SIZEOF_FLOAT) check_type_size("long double" SIZEOF_LONG_DOUBLE) test_big_endian(IS_BIGENDIAN) if(IS_BIGENDIAN) set(WORDS_BIGENDIAN 1) endif(IS_BIGENDIAN) # configuration file configure_file(config-cmake.h.in ${CMAKE_BINARY_DIR}/config.h) include_directories(${CMAKE_BINARY_DIR}) add_definitions(-DHAVE_CONFIG_H) 

因此,您必须提供一个config-cmake.h.in模板,该模板将由cmake处理以生成一个包含所需定义的config.h文件:

 /* Define to 1 if you have the <sys/types.h> header file. */ #cmakedefine HAVE_SYS_TYPES_H /* Define to 1 if you have the <stdint.h> header file. */ #cmakedefine HAVE_STDINT_H /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ #cmakedefine WORDS_BIGENDIAN /* The size of `double', as computed by sizeof. */ #define SIZEOF_DOUBLE @SIZEOF_DOUBLE@ /* The size of `float', as computed by sizeof. */ #define SIZEOF_FLOAT @SIZEOF_FLOAT@ /* The size of `long double', as computed by sizeof. */ #define SIZEOF_LONG_DOUBLE @SIZEOF_LONG_DOUBLE@ 

我邀请你去cmake网站了解更多关于这个工具。

我个人是cmake的粉丝,我正在使用我的个人项目。

一种方法是将操作系统特定的标题放到不同的目录中,并通过将-I标志传递给编译器来控制找到哪个目录。

在你的情况下,你只会有

 #include "defs.h" 

使用预处理器本身并没有什么坏处。 它是为了一个原因而创建的,像这样的条件编译/包含是很好的事情之一。

关键是保持你的代码可读。 如果您的代码中包含大量的#ifdef WIN32块,或者您发现自己在大量文件中执行此操作,以下是一些清理代码的技巧。

1)将你的条件包含移动到一个单独的文件中。 你的源代码文件将会简单地包含#include <defs.h> ,并将你的条件包含块移动到defs.h(即使这个文件中包含了这些)。

2)让你的makefile确定包含哪个文件。 再一次,你的源文件将简单地#include <defs.h> 。 您的makefile会检测当前平台,并根据需要将-iwin32-ilinux添加到编译器选项字符串中。 这是我通常采取的方法。 您可以在makefile或配置阶段(如果您动态生成makefile)执行平台检测工作。 我也发现这个选项在以后的日子里增加对新平台的支持是最简单的; 它似乎最大限度地减少了需要进行的更改次数。

3)如果你正在建立一个Linux系统(意味着任何Win32代码将由交叉编译器生成),你可以让你的编译脚本创建一个符号链接到适当的头文件目录。 您的代码可以引用#include <platform\defs.h>目录,并让makefile或configure脚本担心符合链接正确的文件夹。

预处理器本身并不是一件坏事。 这是一个功能强大的工具,就像许多其他开发工具一样。 问题是你如何使用它?

如果您的预处理过程清晰,文档化和逻辑化,那么这根本就不成问题。 这是当你开始做“聪明”的技巧,难以辨认,这成为一个问题。

理想情况下,你会限制你这样做的频率。 所以你可能会有一个defs.h ,包含在你的所有文件中,而在这个文件中你可以执行你的操作系统特定的逻辑。

这实际上取决于特定平台的实现之间的实际差异的粒度(在您的代码中,这对应于“此处常见的东西”)。

当差异相当大时,我通常更倾向于将所有与平台相关的代码分解为共享相同平台独立接口(.hpp)的多个组件(.cpp)。

例如,假设我们需要从跨平台应用程序中的火线摄像头获取图像。 在不同的平台上实现这一点的方法是绝望的不同。 然后有一个共同的接口将被放置在CameraIeee1394.hpp,并将两个平台相关的实现放入CameraIeee1394_unix.cpp和CameraIeee1394_w32.cpp是有意义的。

为特定平台准备makefile的脚本可以通过简单地检查文件名后缀来自动忽略外部平台的源文件。

如果你打算使用预处理器,现在是使用它的时候了。 有不同的解决方案,但我相信你可以用一个干净的方式解决这个问题。

我通常解决这个问题的方法是首先创建一个包含平台和编译器检测的头文件,例如:

 #define LIB_PLATFORM_WIN32 (1) #define LIB_PLATFORM_LINUX (2) #if defined(WIN32) #define LIB_PLATFORM LIB_PLATFORM_WIN32 #elif defined(LINUX) #define LIB_PLATFORM LIB_PLATFORM_LINUX #endif 

现在你已经有了你的平台。 那么你可以在整个代码中使用自己的定义。

例如,如果你想要一个独立于编译器的64位整数,你可以使用这个

 #if LIB_COMPILER == LIB_COMPILER_MSVC #define int64 __int64 #define uint64 unsigned __int64 #else #define int64 long long #define uint64 unsigned long long #endif 

如果你保持良好的结构,你不应该有一个问题保持清洁和理智。 🙂

你也可以使用typedefs,但是如果你想在应用程序中稍后重载一个类型的话,可能会带来其他问题。