在定义标头所依赖的宏时,例如_FILE_OFFSET_BITS
,FUSE_USE_VERSION
,_GNU_SOURCE
等,放置它们的最佳位置在哪里?
我考虑过的一些可能性包括
CPPFLAGS
级别定义? (例如-D_FILE_OFFSET_BITS=64
):
整个源代码回收
整个项目
只是需要它的来源注意:适用于制作,自动工具和其他构建系统的合理性是我决定的一个因素。
如果宏影响系统头,它们可能应该到它们影响包含那些系统头的每个源文件的某个地方(包括间接包含它们的那些)。因此,最合乎逻辑的位置将在命令行上,假设您的构建系统允许您设置例如CPPFLAGS影响每个文件的编译。
如果你使用预编译的头文件,并且必须首先包含在每个源文件中的预编译头文件(例如,用于MSVC项目的stdafx.h),那么你也可以将它们放在那里。
对于影响自包含库(无论是第三方还是由您编写)的宏,我将创建一个包装器头,用于定义宏,然后包含库头。然后,项目中库的所有使用都应包含您的包装器头,而不是直接包含库头。这样可以避免不必要地定义宏,并清楚地表明它们与该库有关。如果库之间存在依赖关系,那么您可能希望使宏全局(在构建系统或预编译头中)只是为了安全起见。
这得看情况。
大多数,我通过命令行定义 - 在Makefile或你使用的任何构建系统。
至于_FILE_OFFSET_BITS
,我真的不会明确定义它,而是使用getconf LFS_CFLAGS
和getconf LFS_LDFLAGS
。
我总是通过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_LARGEFILE
和AC_FUNC_FSEEKO
:
- Macro:
AC_SYS_LARGEFILE
安排64位文件偏移,称为大文件支持。在某些主机上,必须使用特殊的编译器选项来构建可以访问大文件的程序。将任何此类选项附加到输出变量
CC
。必要时定义_FILE_OFFSET_BITS
和_LARGE_FILES
。通过使用
--disable-largefile
选项进行配置,可以禁用大文件支持。如果您使用此宏,请检查您的程序是否在
off_t
比long int
更宽时工作,因为这在启用大文件支持时很常见。例如,用off_t
打印任意X
值printf("%ld", (long int) X)
是不正确的。LFS引入了
fseeko
和ftello
函数来替换不使用fseek
的C对应的ftell
和off_t
。使用时,请注意使用AC_FUNC_FSEEKO
使其原型可用,并启用大文件支持。
如果你使用autoheader
生成config.h
,你可以使用AC_DEFINE
或AC_DEFINE_UNQUOTED
定义你关心的其他宏:
AC_DEFINE([FUSE_VERSION], [28], [FUSE Version.])
如果您正在使用autoheader,则定义将传递到命令行或放在config.h
中。 AC_DEFINE
的真正好处在于,它可以轻松地允许预处理器定义作为配置检查的结果,并将系统特定的重要部分与重要细节分开。
首先编写.c
文件,#include "config.h"
,然后是接口标题(例如,foo.h
用于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 <stdio.h>
#define mydefine 7
#include "header_that_uses_mydefine.h"
#define mydefine 7
#include "another_header_that_uses_mydefine.h"
请注意,我已经完成了两次,因此它更加本地化。这不是问题,因为如果您只更改一个,编译器会告诉您它,但它确保您知道为特定标头设置了这些定义。
并且,如果您确定在没有首先将bitio.h
设置为8的情况下永远不会包含(例如)BITCOUNT
,您甚至可以创建一个只包含以下内容的bitio8.h
文件:
#define BITCOUNT 8
#include "bitio.h"
然后在源文件中包含bitio8.h
。
目标特定的全局,项目范围的常量最好放在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
我建议使用头文件,因为它允许您拥有由make文件和其他构建系统以及IDE项目(如Visual Studio)构建的代码库。这给你一个单点的定义,可以伴随着评论(我是doxygen的粉丝,允许你生成macro documentation)。
头文件的另一个好处是,您可以轻松编写单元测试,以验证是否只定义了有效的宏组合。