13

When defining macros that headers rely on, such as _FILE_OFFSET_BITS, FUSE_USE_VERSION, _GNU_SOURCE among others, where is the best place to put them?

Some possibilities I've considered include

  1. At the top of the any source files that rely on definitions exposed by headers included in that file
  2. Immediately before the include for the relevant header(s)
  3. Define at the CPPFLAGS level via the compiler? (such as -D_FILE_OFFSET_BITS=64) for the:
    1. Entire source repo
    2. The whole project
    3. Just the sources that require it
  4. In project headers, which should also include those relevant headers to which the macros apply
  5. Some other place I haven't thought of, but is infinitely superior

A note: Justification by applicability to make, autotools, and other build systems is a factor in my decision.

4

8 回答 8

3

这得看情况。

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

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

于 2010-08-10T13:53:36.010 回答
3

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

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

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

于 2010-08-11T08:18:01.280 回答
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函数来替换它们的 C 对应物fseek并且ftell不使用off_t. 在使用它们并启用大文件支持时,请注意使用AC_FUNC_FSEEKO以使它们的原型可用。

如果您autoheader用于生成 a config.h,则可以定义您关心的其他宏使用AC_DEFINEor AC_DEFINE_UNQUOTED

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

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

写入.c文件时,#include "config.h"首先是接口头(例如,foo.hfor foo.c- 这确保头没有缺少依赖项),然后是所有其他头。

于 2010-08-24T10:02:15.237 回答
1

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

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

于 2010-08-02T05:50:36.500 回答
1

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

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

跟上它们当然很烦人。

于 2010-08-10T13:48:13.367 回答
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

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


`#include "bool.h"`
// blah
于 2010-08-11T19:45:26.840 回答
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"

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


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

#define BITCOUNT 8
#include "bitio.h"

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

于 2010-08-11T04:24:40.637 回答
0

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

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

于 2010-08-11T04:35:37.597 回答