有没有一种方法可以实现在重命名头文件时不必修改的头保护?
Is there a way to implement a header guard that doesn't have to be modified when the header file is renamed?
目前对于名为 test_header.h 的头文件,我使用 -
#ifndef TEST_HEADER_H
#define TEST_HEADER_H
/* code */
#endif /* TEST_HEADER_H */
我想要的是一个不直接使用文件名的header guard。像(即一厢情愿的粗略假设解决方案)-
#if __FILE__ not in INCLUDE_LIST
#APPEND(INCLUDE_LIST, __FILE__)
/* code */
#endif
你可以随意命名 header 守卫。
- 如果您的 header 与某个主题相关,请以该主题命名,即
LIST_OPERATIONS
- 省略守卫并编写一个脚本,根据当前文件名插入它们。 运行 此脚本作为编译前构建过程的一部分。 (记住要么创建一个修改过的副本,要么在构建之后删除包含,否则你最终会在你的 header 中得到很多包含保护)
- 根据您的项目应使用的编译器,它们可能支持
#pragma once
方法。
它非常便携,得到所有主要编译器以及 14 个编译器中的 13 个 (according to Wikipedia) 的良好支持。
另请查看 #pragma once vs include guards?。
正如其他人所指出的,您用作 header 守卫的东西本质上并不重要;它只需要在可能永远是 co-included.
的 header 集合中是唯一的
您可以创建一个 UUID 或 GUID 并将其用作 header 保护(或某种散列 — MD5、SHA1、SHA2、SHA3 等)。唯一的技巧是处理前导数字的可能性;这很容易解决(我使用 H_
作为前缀)。
虽然大多数情况下,我使用基于文件名的名称,并且通常不会经常重命名 headers 以至于这是一个问题。
这是一个名为 hdrguard
的脚本,我用它来为给定的 header 文件生成 header 保护线:
#!/bin/sh
#
# @(#)$Id: hdrguard.sh,v 1.8 2016/05/09 18:41:57 jleffler Exp $
#
# Generate #ifndef sequence to guard header against multiple inclusion
arg0=$(basename [=10=] .sh)
usestr="Usage: $arg0 [-bdfhimV] header.h [...]"
usage()
{
echo "$usestr" 1>&2
exit 1
}
help()
{
echo "$usestr"
echo
echo " -b Use base name of file for guard"
echo " -d Use _DOT_H after name (instead of _H)"
echo " -f Use specified path name of file for guard (default)"
echo " -h Print this help message and exit"
echo " -i Omit _INCLUDED after name"
echo " -m Generate MD5 hash value as header guard"
echo " -V Print version information and exit"
exit 0
}
opt_incl=yes
opt_base=no
opt_dot=no
opt_md5=no
while getopts bdfhimV opt
do
case "$opt" in
(b) opt_base=yes;;
(d) opt_dot=yes;;
(f) opt_base=no;;
(h) help;;
(i) opt_incl=no;;
(m) opt_md5=yes;;
(V) echo "$arg0: HDRGUARD Version "'$Revision: 1.8 $ ($Date: 2016/05/09 18:41:57 $)' | rcsmunger; exit 0;;
(*) usage;;
esac
done
shift $(($OPTIND - 1))
[ $# -eq 0 ] && usage
for i in "$@"
do
if [ $opt_base = yes ]
then i=$(basename $i)
fi
if [ $opt_dot = yes ]
then i=$(echo "$i" | sed 's/\.h$/_dot_h/')
fi
i=$(echo $i | tr 'a-z' 'A-Z' | tr -s '/+.-' '____' | sed 's/^_//')
if [ $opt_incl = yes ]
then
case "$i" in
(*_INCLUDED)
: OK;;
(*)
i="${i}_INCLUDED";;
esac
fi
if [ $opt_md5 = yes ]
then
tmp=$(mktemp ./hdrgrd.XXXXXXXX)
trap "rm -f $tmp; exit 1" 0 1 2 3 13 15
echo "$i.$(isodate compact)" > "$tmp"
i=$(md5 "$tmp" | sed 'y/abcdef/ABCDEF/; s/\([^ ]*\) .*/H_/')
rm -f "$tmp"
trap 0 1 2 3 13 15
fi
echo
echo "#ifndef $i"
echo "#define $i"
echo
echo "#endif /* $i */"
echo
done
它没有 SHA1、SHA2 或 SHA3 的代码 — 它可以选择使用 MD5(以命令形式 md5
)。添加对替代哈希算法的支持并不是很难。它不需要文件存在。
示例使用:
$ hdrguard header.h
#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
#endif /* HEADER_H_INCLUDED */
$ hdrguard -m header.h
#ifndef H_6DC5070597F88701EB6D2CCAACC73383
#define H_6DC5070597F88701EB6D2CCAACC73383
#endif /* H_6DC5070597F88701EB6D2CCAACC73383 */
$
我经常在 vim
中使用它,在光标位于空行时键入 !!hdrguard %
之类的命令以生成适合 [=58] 的 header 守卫=] 我正在编辑。这也是它在顶部和底部生成空白行的原因。
该命令使用脚本 isodate
和 rcsmunger
。使用参数 compact
,isodate
命令等效于:
date +'%Y%m%d.%H%M%S'
完整的命令支持多种替代格式,并且比必须在任何地方键入 date
命令更简洁。您完全可以自由地放弃使用单独的脚本,而只是将显示的扩展嵌入到 hdrguard
中。实际上,您可以只使用 date
就可以了;它只是散列运算的种子material,使被散列的数据唯一。
$ isodate compact
20161228.185232
$
rcsmunger
命令只是将 RCS ID 字符串转换为我喜欢的报告版本信息的格式:
#!/usr/bin/env perl -p
#
# @(#)$Id: rcsmunger.pl,v 1.9 2015/11/02 23:54:32 jleffler Exp $
#
# Remove the keywords around the values of RCS keywords
use strict;
use warnings;
# Beware of RCS hacking at RCS keywords!
# Convert date field to ISO 8601 (ISO 9075) notation
s%$(Date:) (\d\d\d\d)/(\d\d)/(\d\d) (\d\d:\d\d:\d\d) $%$ -- $%go;
# Remove keywords
s/$([A-Z][a-z]+|RCSfile): ([^$]+) $//go;
例如:
$ hdrguard -V
hdrguard: HDRGUARD Version 1.8 (2016-05-09 18:41:57)
$
版本信息的打印可以看作是old-school版本控制;如果您使用 git
之类的 DVCS,则必须以不同的方式完成,这是我没有为我的个人软件 collection.[=31 进行批发迁移到 git
的原因之一=]
目前对于名为 test_header.h 的头文件,我使用 -
#ifndef TEST_HEADER_H
#define TEST_HEADER_H
/* code */
#endif /* TEST_HEADER_H */
我想要的是一个不直接使用文件名的header guard。像(即一厢情愿的粗略假设解决方案)-
#if __FILE__ not in INCLUDE_LIST
#APPEND(INCLUDE_LIST, __FILE__)
/* code */
#endif
你可以随意命名 header 守卫。
- 如果您的 header 与某个主题相关,请以该主题命名,即
LIST_OPERATIONS
- 省略守卫并编写一个脚本,根据当前文件名插入它们。 运行 此脚本作为编译前构建过程的一部分。 (记住要么创建一个修改过的副本,要么在构建之后删除包含,否则你最终会在你的 header 中得到很多包含保护)
- 根据您的项目应使用的编译器,它们可能支持
#pragma once
方法。
它非常便携,得到所有主要编译器以及 14 个编译器中的 13 个 (according to Wikipedia) 的良好支持。
另请查看 #pragma once vs include guards?。
正如其他人所指出的,您用作 header 守卫的东西本质上并不重要;它只需要在可能永远是 co-included.
的 header 集合中是唯一的您可以创建一个 UUID 或 GUID 并将其用作 header 保护(或某种散列 — MD5、SHA1、SHA2、SHA3 等)。唯一的技巧是处理前导数字的可能性;这很容易解决(我使用 H_
作为前缀)。
虽然大多数情况下,我使用基于文件名的名称,并且通常不会经常重命名 headers 以至于这是一个问题。
这是一个名为 hdrguard
的脚本,我用它来为给定的 header 文件生成 header 保护线:
#!/bin/sh
#
# @(#)$Id: hdrguard.sh,v 1.8 2016/05/09 18:41:57 jleffler Exp $
#
# Generate #ifndef sequence to guard header against multiple inclusion
arg0=$(basename [=10=] .sh)
usestr="Usage: $arg0 [-bdfhimV] header.h [...]"
usage()
{
echo "$usestr" 1>&2
exit 1
}
help()
{
echo "$usestr"
echo
echo " -b Use base name of file for guard"
echo " -d Use _DOT_H after name (instead of _H)"
echo " -f Use specified path name of file for guard (default)"
echo " -h Print this help message and exit"
echo " -i Omit _INCLUDED after name"
echo " -m Generate MD5 hash value as header guard"
echo " -V Print version information and exit"
exit 0
}
opt_incl=yes
opt_base=no
opt_dot=no
opt_md5=no
while getopts bdfhimV opt
do
case "$opt" in
(b) opt_base=yes;;
(d) opt_dot=yes;;
(f) opt_base=no;;
(h) help;;
(i) opt_incl=no;;
(m) opt_md5=yes;;
(V) echo "$arg0: HDRGUARD Version "'$Revision: 1.8 $ ($Date: 2016/05/09 18:41:57 $)' | rcsmunger; exit 0;;
(*) usage;;
esac
done
shift $(($OPTIND - 1))
[ $# -eq 0 ] && usage
for i in "$@"
do
if [ $opt_base = yes ]
then i=$(basename $i)
fi
if [ $opt_dot = yes ]
then i=$(echo "$i" | sed 's/\.h$/_dot_h/')
fi
i=$(echo $i | tr 'a-z' 'A-Z' | tr -s '/+.-' '____' | sed 's/^_//')
if [ $opt_incl = yes ]
then
case "$i" in
(*_INCLUDED)
: OK;;
(*)
i="${i}_INCLUDED";;
esac
fi
if [ $opt_md5 = yes ]
then
tmp=$(mktemp ./hdrgrd.XXXXXXXX)
trap "rm -f $tmp; exit 1" 0 1 2 3 13 15
echo "$i.$(isodate compact)" > "$tmp"
i=$(md5 "$tmp" | sed 'y/abcdef/ABCDEF/; s/\([^ ]*\) .*/H_/')
rm -f "$tmp"
trap 0 1 2 3 13 15
fi
echo
echo "#ifndef $i"
echo "#define $i"
echo
echo "#endif /* $i */"
echo
done
它没有 SHA1、SHA2 或 SHA3 的代码 — 它可以选择使用 MD5(以命令形式 md5
)。添加对替代哈希算法的支持并不是很难。它不需要文件存在。
示例使用:
$ hdrguard header.h
#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
#endif /* HEADER_H_INCLUDED */
$ hdrguard -m header.h
#ifndef H_6DC5070597F88701EB6D2CCAACC73383
#define H_6DC5070597F88701EB6D2CCAACC73383
#endif /* H_6DC5070597F88701EB6D2CCAACC73383 */
$
我经常在 vim
中使用它,在光标位于空行时键入 !!hdrguard %
之类的命令以生成适合 [=58] 的 header 守卫=] 我正在编辑。这也是它在顶部和底部生成空白行的原因。
该命令使用脚本 isodate
和 rcsmunger
。使用参数 compact
,isodate
命令等效于:
date +'%Y%m%d.%H%M%S'
完整的命令支持多种替代格式,并且比必须在任何地方键入 date
命令更简洁。您完全可以自由地放弃使用单独的脚本,而只是将显示的扩展嵌入到 hdrguard
中。实际上,您可以只使用 date
就可以了;它只是散列运算的种子material,使被散列的数据唯一。
$ isodate compact
20161228.185232
$
rcsmunger
命令只是将 RCS ID 字符串转换为我喜欢的报告版本信息的格式:
#!/usr/bin/env perl -p
#
# @(#)$Id: rcsmunger.pl,v 1.9 2015/11/02 23:54:32 jleffler Exp $
#
# Remove the keywords around the values of RCS keywords
use strict;
use warnings;
# Beware of RCS hacking at RCS keywords!
# Convert date field to ISO 8601 (ISO 9075) notation
s%$(Date:) (\d\d\d\d)/(\d\d)/(\d\d) (\d\d:\d\d:\d\d) $%$ -- $%go;
# Remove keywords
s/$([A-Z][a-z]+|RCSfile): ([^$]+) $//go;
例如:
$ hdrguard -V
hdrguard: HDRGUARD Version 1.8 (2016-05-09 18:41:57)
$
版本信息的打印可以看作是old-school版本控制;如果您使用 git
之类的 DVCS,则必须以不同的方式完成,这是我没有为我的个人软件 collection.[=31 进行批发迁移到 git
的原因之一=]