标题的宏定义,放置它们的位置?

在定义标头所依赖的宏时,例如_FILE_OFFSET_BITSFUSE_USE_VERSION_GNU_SOURCE等等,放置它们的最佳位置在哪里?

我考虑过的一些可能性包括

  1. 位于依赖于该文件中包含的标头所公开的定义的任何源文件的顶部
  2. 紧接着相关标题的包含之前
  3. 通过编译器定义CPPFLAGS级别? (例如-D_FILE_OFFSET_BITS=64 ):
    1. 整个源代码回收
    2. 整个项目
    3. 只是需要它的来源
  4. 在项目标题中,还应包括宏应用的那些相关标题
  5. 其他一些我没有想过的地方,但是无限优越

注意:适用于制作,自动工具和其他构建系统的合理性是我决定的一个因素。

如果宏影响系统头,它们可能应该到它们影响包含那些系统头的每个源文件的某个地方(包括间接包含它们的那些)。 因此,最合乎逻辑的位置将在命令行上,假设您的构建系统允许您设置例如CPPFLAGS以影响每个文件的编译。

如果你使用预编译的头文件,并且必须首先包含在每个源文件中的预编译头文件(例如,用于MSVC项目的stdafx.h),那么你也可以将它们放在那里。

对于影响自包含库(无论是第三方还是由您编写)的宏,我将创建一个包装器头,用于定义宏,然后包含库头。 然后,项目中库的所有使用都应该包含您的包装器头,而不是直接包含库头。 这样可以避免不必要地定义宏,并清楚地表明它们与该库有关。 如果库之间存在依赖关系,那么您可能希望将宏(在构建系统或预编译头中)设置为安全端。

这得看情况。

大多数,我通过命令行定义 – 在Makefile或你使用的任何构建系统。

至于_FILE_OFFSET_BITS我真的不会明确定义它,而是使用getconf LFS_CFLAGSgetconf LFS_LDFLAGS

我总是通过CPPFLAGS将它们放在命令行CPPFLAGS用于整个项目。 如果你将它们放在任何其他地方,那么你可能会忘记将它们复制到新的源文件或包含系统标题之前包含定义它们的项目标题,这可能导致非常讨厌的错误(比如一个文件声明)遗留的32位struct stat ,并将其地址传递给另一个需要64位struct stat文件中的函数。

顺便说一下, _FILE_OFFSET_BITS=64仍然不是glibc的默认设置真的很荒谬。

我见过的大多数项目都是通过-D命令行选项来实现的。 他们在那里是因为它可以使用不同的编译器和系统头来轻松构建源代码。 如果您使用系统编译器为另一个不需要它们的系统构建或需要不同的系统,则配置脚本可以轻松更改make文件传递给编译器的命令行参数。

它可能最适合整个程序,因为一些标志会影响函数的哪个版本,或者结构的大小/布局,如果你不小心,可能会混淆这些东西。

他们肯定很烦人跟上。

对于_GNU_SOURCE和特别是autotools,你可以使用AC_USE_SYSTEM_EXTENSIONS (在这里引用autoconf手册):

– Macro: AC_USE_SYSTEM_EXTENSIONS
这个宏是在Autoconf 2.60中引入的。 如果可能,请在通常禁用扩展的主机上启用C或Posix扩展,通常是由于标准一致性命名空间问题。 应该在运行C编译器的任何宏之前调用它。 适当时定义以下预处理器宏:

_GNU_SOURCE在GNU / Linux上启用扩展。

__EXTENSIONS__在Solaris上启用常规扩展。

_POSIX_PTHREAD_SEMANTICS在Solaris上启用线程扩展。

_TANDEM_SOURCE为HP NonStop平台启用扩展。

_ALL_SOURCE为AIX 3和Interix启用扩展。

_POSIX_SOURCE为Minix启用Posix函数。

_POSIX_1_SOURCE为Minix启用其他Posix函数。

_MINIX识别Minix平台。 此特定预处理器宏已过时,可能会在将来的Autoconf版本中删除。

对于_FILE_OFFSET_BITS ,您需要调用AC_SYS_LARGEFILEAC_FUNC_FSEEKO

– Macro: AC_SYS_LARGEFILE

安排64位文件偏移,称为大文件支持。 在某些主机上,必须使用特殊的编译器选项来构建可以访问大文件的程序。 将任何此类选项附加到输出变量CC 。 如有必要,定义_FILE_OFFSET_BITS_LARGE_FILES

通过使用--disable-largefile选项进行配置,可以禁用大文件支持。

如果使用此宏,请检查程序是否正常工作,即使off_tlong int宽,因为这在启用大文件支持时很常见。 例如,使用printf("%ld", (long int) X)打印任意off_tX是不正确的。

LFS引入了fseekoftello函数来替换不使用off_t的C对应fseekftell 。 在使用AC_FUNC_FSEEKO ,请AC_FUNC_FSEEKO使用AC_FUNC_FSEEKO使其原型可用,并启用大文件支持。

如果您使用autoheader生成config.h ,您可以使用AC_DEFINEAC_DEFINE_UNQUOTED定义您关心的其他宏:

 AC_DEFINE([FUSE_VERSION], [28], [FUSE Version.]) 

如果您正在使用autoheader,则定义将传递到命令行或放在config.hAC_DEFINE的真正好处在于,它可以轻松地允许预处理器定义作为配置检查的结果,并将特定于系统的缺陷与重要细节分开。

在编写.c文件时,首先是#include "config.h" ,然后是接口头(例如, foo.c用于foo.c – 这可以确保头没有丢失的依赖关系),然后是所有其他头。

我通常会尽可能地将它们放在需要它们的东西上,同时确保不要错误地设置它们。

相关信息保持密切,以便于识别。 一个典型的例子是C现在允许在代码中的任何位置而不是仅仅在函数顶部的变量定义的能力:

 void something (void) { // 600 lines of code here int x = fn(y); // more code here } 

比以下更好:

 void something (void) { int x; // 600 lines of code here x = fn(y); // more code here } 

因为在后一种情况下你不必去寻找x的类型。


举例来说,如果需要使用不同的值多次编译单个源文件,则必须使用编译器执行此操作:

 gcc -Dmydefine=7 -o binary7 source.c gcc -Dmydefine=9 -o binary9 source.c 

但是,如果该文件的每个编译都使用7,则可以将其移动到更接近使用它的位置:

 source.c: #include  #define mydefine 7 #include "header_that_uses_mydefine.h" #define mydefine 7 #include "another_header_that_uses_mydefine.h" 

请注意,我已经完成了两次,因此它更加本地化。 这不是问题,因为如果您只更改一个,编译器会告诉您它,但它确保您知道为特定标头设置了这些定义。


而且,如果您确定在没有先将BITCOUNT设置为8的情况下永远不会包含(例如) bitio.h ,您甚至可以创建一个bitio8.h包含以下内容的bitio8.h文件:

 #define BITCOUNT 8 #include "bitio.h" 

然后在源文件中包含bitio8.h

我建议使用头文件,因为它允许您拥有由make文件和其他构建系统以及IDE项目(如Visual Studio)构建的代码库。 这为您提供了单点定义,可以附带注释(我是Doxygen的粉丝,可以生成宏文档 )。

头文件的另一个好处是,您可以轻松编写unit testing,以validation是否只定义了有效的宏组合。

特定于目标的全局项目范围常量最好放在makefile中的CCFLAGS中。 您在整个地方使用的常量可以放入适当的头文件,这些文件包含在使用它们的任何文件中。

例如,

 // bool.h - a boolean type for C #ifndef __BOOL_H__ #define BOOL_H typedef int bool_t #define TRUE 1 #define FALSE 0 #endif 

然后,在其他一些标题中,

 `#include "bool.h"` // blah