1

I've got a known, predetermined set of calls to a function

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

And what I was hoping to do was something like

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

So that the whole thing gets replaced by the integer before compiling and I can switch off the value and not have to store the string or do the comparaison at run-time. I realize that we can't do this in the preprocessor but was just wondering if anyone has come across some nifty way of getting around this seemingly solveable problem.

schuess
  • 1,009
  • 1
  • 10
  • 21
  • Is it important that your arguments to the macro are strings? Couldn't it just be `FUNC_A(ABCD); FUNC_A(EFGH);` with tokens instead of strings? – Jens Gustedt Oct 02 '12 at 14:54
  • Yeah unfortunatly I need to use symbols in addition to letters that the preprocessor cannot handle (eg. ==, > < etc...) – schuess Oct 02 '12 at 15:07
  • The only thing I can think of is a custom parser to replace them before compilation but I really don't want to change the source – schuess Oct 02 '12 at 15:08

2 Answers2

1

You may handcraft your comparison if you need that, but this will be tedious. For simplicity let us suppose that we want to do it for the string "AB":

#define testAB(X) ((X) && (X)[0] == 'A' && (X)[1] == 'B' && !(X)[2])

this will return 1 when the string is equal to "AB" and 0 otherwise, and also take care that the string is of the correct length, not access beyond array bounds etc.

The only thing that you'd have to worry, is that the argument X is evaluated multiple times. This isn't a problem if you pass in a string literal, but would be for expressions with side effects.

For string literals any decent compiler should be able to replace such an expression at compile time.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
0

For doing as you describe, avoiding strings and run-time comparisons, I can only think of a pre-preprocessor. Would it be for just a quick hacking around, in a Unix environment I'd try a simple wrapper for the preprocessor using a bash script that in turn uses sed or awk to replace the functions and arguments mentioned and then calling the real cpp preprocessor. I'd consider this just as a quick hack.

Update: In linux and gcc, it seems easier to do a post-preprocessor, because we can replace the generated .i file (but we can't generally do that with the original .c file). For doing that, we can make a cc1 wrapper.

Warning: this is another dangerous and ugly hack. Also see Custom gcc preprocessor

This is a cc1 wrapper for doing that. It's a bash script for linux and gcc 4.6:

#!/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

How to use it: call gcc using flags -B (directory where cc1 wrapper resides) and --no-integrated-cpp

Community
  • 1
  • 1
German Garcia
  • 1,209
  • 14
  • 14