【问题标题】:Is there a way to implement a header guard that doesn't have to be modified when the header file is renamed? [closed]有没有办法实现头文件重命名时不必修改的头文件? [关闭]
【发布时间】:2016-12-28 16:15:46
【问题描述】:

目前对于名为 test_header.h 的头文件我使用 -

#ifndef TEST_HEADER_H
#define TEST_HEADER_H   
/* code */
#endif /* TEST_HEADER_H */

我想要的是一个不直接使用文件名的标头保护。类似的东西(即一厢情愿的粗略假设解决方案)-

#if __FILE__ not in INCLUDE_LIST
#APPEND(INCLUDE_LIST, __FILE__)
/* code */
#endif

【问题讨论】:

  • 严格来说,重命名后您不需要更改标头保护 - 除非您有另一个文件取代旧名称。
  • 您可以使用带有前缀和 UUID 的标识符,更加省心。
  • #pragma once 被许多编译器支持...
  • #pragma once 包含保护转换器,反之亦然。 Qt Creator recently used one to convert all their headers to use #pragma once
  • @work.bin,有一些编译指示实际上是由标准 C 定义的——对于那些特定的编译指示来说,可移植性不是问题。但#pragma once 不在其中,我倾向于同意避免实现定义的行为的想法,例如使用非标准编译指示产生。

标签: c c-preprocessor header-files


【解决方案1】:

正如其他人所指出的,您用作标头守卫的本质并不重要。它只需要在可能被共同包含的一组标头中是唯一的。

您可以创建一个 UUID 或 GUID 并将其用作标头保护(或某种散列 - MD5、SHA1、SHA2、SHA3 ......)。唯一的技巧是处理前导数字的可能性;这很容易解决(我使用H_ 作为前缀)。

但大多数情况下,我使用基于文件名的名称,并且通常不会频繁地重命名标题,这会造成问题。

这是一个名为 hdrguard 的脚本,我用它来为给定的头文件生成头保护行:

#!/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 $0 .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_\1/')
        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 % 之类的命令以生成适合我正在编辑的标题的标题保护。这就是为什么它也会在顶部和底部生成空行。

该命令使用脚本isodatercsmunger。使用参数compactisodate 命令等效于:

date +'%Y%m%d.%H%M%S'

完整的命令支持多种替代格式,并且比必须在任何地方输入date 命令更简洁。您完全可以放弃使用单独的脚本,而只是将显示的扩展嵌入到hdrguard 中。事实上,你可以只使用date 就可以了;它只是散列操作的种子材料,使被散列的数据唯一。

$ 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) \$%\$$1 $2-$3-$4 $5 \$%go;
# Remove keywords
s/\$([A-Z][a-z]+|RCSfile): ([^\$]+) \$/$2/go;

例如:

$ hdrguard -V
hdrguard: HDRGUARD Version 1.8 (2016-05-09 18:41:57)
$

您可以将版本信息的打印视为老式的版本控制;如果您使用诸如 git 之类的 DVCS,则必须以不同的方式完成,这也是我没有为我的个人软件集批量迁移到 git 的原因之一。

【讨论】:

    【解决方案2】:

    你可以随意命名标题守卫。

    • 如果您的标题与某个主题相关,请在其后命名,即LIST_OPERATIONS
    • 省略守卫并编写一个脚本,根据当前文件名插入它们。在编译之前将此脚本作为构建过程的一部分运行。 (请记住在构建后创建修改后的副本或删除包含,否则您最终会在标题中包含很多包含保护)
    • 根据应用于您的项目的编译器,它们可能支持#pragma once 方法。

    【讨论】:

    • 如果旧的包含保护具有独特的格式,您的脚本也可以删除它们,或者如果正确的保护已经存在,则什么也不做
    【解决方案3】:

    #pragma once

    它非常可移植,受到所有主要编译器以及 14 个编译器中的 13 个 (according to Wikipedia) 的良好支持。

    还可以查看#pragma once vs include guards?

    【讨论】:

    • 但是使用#pragma once 的程序仍然不是严格遵守的。如果您的目标是编写可移植代码,那么您将不会使用它,尽管支持它的主要编译器的比例。
    猜你喜欢
    • 1970-01-01
    • 2019-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 1970-01-01
    • 2021-11-20
    相关资源
    最近更新 更多