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

时间:2010-08-02 03:04:22

标签: c macros header autotools build-system

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

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

  1. 依赖于该文件中包含的标题所公开的定义的任何源文件的顶部
  2. 紧接相关标题的包含之前
  3. 通过编译器定义CPPFLAGS级别? (例如-D_FILE_OFFSET_BITS=64):
    1. 整个源代码
    2. 整个项目
    3. 只是需要它的来源
  4. 在项目标题中,其中还应包括宏应用的相关标题
  5. 我没有想过的其他地方,但是无限优越
  6. 注意:适用于制作,自动工具和其他构建系统的理由是我决定的一个因素。

8 个答案:

答案 0 :(得分:3)

嗯,这取决于。

大多数情况下,我通过命令行定义 - 在Makefile或您使用的任何构建系统中。

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:1)

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

顺便说一句,_FILE_OFFSET_BITS=64仍然不是glibc的默认值,这真是荒谬。

答案 3 :(得分:1)

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

最好对整个程序执行此操作,因为某些标志会影响函数的哪个版本或结构的大小/布局并将它们混合在一起可能会导致疯狂的事情,如果你不小心的话。

他们肯定很烦人。

答案 4 :(得分:1)

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

  

- 微距: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

  

- 微距:AC_SYS_LARGEFILE

     

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

     

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

     

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

     

LFS引入了fseekoftello函数来替换不使用fseek的C对等ftelloff_t。使用AC_FUNC_FSEEKO时要小心使用原型,并启用大文件支持。

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

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

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

首先编写.c文件#include "config.h",然后是foo.h的接口标头(例如foo.c - 这可以确保标头没有遗漏的依赖关系),所有其他标题。

答案 5 :(得分:0)

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

的相关信息应保持接近,以便更容易识别。一个典型的例子是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 <stdio.h>

    #define mydefine 7
    #include "header_that_uses_mydefine.h"

    #define mydefine 7
    #include "another_header_that_uses_mydefine.h"

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


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

#define BITCOUNT 8
#include "bitio.h"

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

答案 6 :(得分:0)

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

头文件的另一个好处是,您可以轻松编写单元测试,以验证是否只定义了有效的宏组合。

答案 7 :(得分:0)

目标特定的全局,项目范围的常量最好放在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

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

#ifndef __BOOL_H__