使用预处理器将char数组替换为索引

时间:2012-10-02 14:51:11

标签: c c-preprocessor

我有一组已知的,预定的函数调用

FUNC_A("ABCD");
FUNC_A("EFGH");

我希望做的事情就像是

#define FUNC_A("ABCD")     0
#define FUNC_A("EFGH")     1
#define FUNC_A(X)          0xFF

因此在编译之前整个事物被整数替换,我可以关闭值而不必存储字符串或在运行时执行comparaison。 我意识到我们不能在预处理器中做到这一点,但只是想知道是否有人遇到了一些解决这个看似可解决的问题的好方法。

2 个答案:

答案 0 :(得分:1)

如果你需要,你可以手工制作你的比较,但这将是乏味的。为简单起见,我们假设我们想要为字符串"AB"

执行此操作
#define testAB(X) ((X) && (X)[0] == 'A' && (X)[1] == 'B' && !(X)[2])

当字符串等于1时将返回"AB",否则返回0,并且还要注意字符串的长度是正确的,不能超出数组边界等。

您唯一需要担心的是,参数X被多次评估。如果你传入一个字符串文字,这不是问题,但是对于有副作用的表达式。

对于字符串文字,任何体面的编译器都应该能够在编译时替换这样的表达式。

答案 1 :(得分:0)

如你所描述的那样,避免字符串和运行时比较,我只能想到预处理器。如果只是快速入侵,在Unix环境中,我会尝试使用bash脚本为预处理器设置一个简单的包装器,然后使用sed或awk替换所提到的函数和参数,然后调用真正的cpp预处理器。我认为这只是一个快速的黑客攻击。

更新:在linux和gcc中,执行后预处理器似乎更容易,因为我们可以替换生成的.i文件(但我们通常不能使用原始的.c文件)。为此,我们可以制作一个cc1包装器。

警告:这是另一个危险且难看的黑客。另请参阅Custom gcc preprocessor

这是一个用于执行此操作的cc1包装器。这是linux和gcc 4.6的bash脚本:

#!/bin/bash
# cc1 that does post preprocessing on generated .i files, replacing function calls
#
# note: doing post preprocessing is easier than pre preprocessing, because in post preprocessing we can replace the temporary .i file generated by the preprocessor (in case of doing pre preprocessing, we should change the original .c file -this is unacceptable-; or generate a new temp .c file with our preprocessing before calling the real preprocessor, but then eventual error messages are now referring to the temp .c file..)

convert ()
{  
    local i=$1
    local o=$2

    ascript=$(cat <<- 'EOAWK'
    {
            FUNCT=$1; 
            ARGS=$2; 
            RESULT=$3; 
            printf "s/%s[ \\t]*([ \\t]*%s[ \\t]*)/%s/g\n", FUNCT, ARGS, RESULT;
    } 
EOAWK
    )

    seds=$(awk -F '|' -- "$ascript" << EOFUNCS
FUNC_A|"ABCD"|0
FUNC_A|"EFGH"|1
FUNC_A|X|0xFF
EOFUNCS
    )

    sedfile=$(mktemp --tmpdir prepro.sed.XXX)
    echo -n "$seds" > "$sedfile"

    sed -f "$sedfile" "$i" > "$o"
    rc=$?

    rm "$sedfile"

    return $rc
}

for a
do 
    if [[ $a = -E ]]
    then
            isprepro=1
    elif [[ $isprepro && $a = -o ]]
    then   
            getfile=1
    elif [[ $isprepro && $getfile && $a =~ ^[^-].*[.]i ]]
    then
            ifile=$a
            break
    fi
done

#echo "args:$@"
#echo "getfile=$getfile"
#echo "ifile=$ifile"

realcc1=/usr/lib/gcc/i686-linux-gnu/4.6/cc1
$realcc1 "$@"
rc=$?
if [[ $rc -eq 0 && $isprepro && $ifile ]]
then
    newifile=$(mktemp --tmpdir prepro.XXX.i)
    convert "$ifile" "$newifile" && mv "$newifile" "$ifile"
fi

exit $rc

如何使用它:使用标志 -B (cc1包装器所在的目录)和 - no-integrated-cpp

调用gcc