Itay Hazan
Itay Hazan

Reputation: 15

Pre-processor #if doesn't work

I'm trying to write a somehow-generic printArray function in C, which I will able to run with several programs, each with a different type of array. I did this:

#define TYPE int /* or char or double*/

void printArray(TYPE *a, int size){
    for (int i=0; i<size; i++){
#if TYPE == int
        printf("%d ", a[i]);
#elif TYPE == char
        printf("%c ", a[i]);
#elif TYPE == double
        printf("%f ", a[i]);
#endif
    }
    printf("\n");
}

I have tried running it, but no matter what TYPE is defined to be, the first #if always checks in, meaning - if the if's are written:

#if TYPE == int
        printf("int");
#elif TYPE == char
        printf("char");
#elif TYPE == double
        printf("double");
#endif

then it will print "int", even if TYPE is defined to be char, and if

#if TYPE == char
        printf("char");
#elif TYPE == int 
        printf("int");
#elif TYPE == double
        printf("double");
#endif

then it will print "char", even if TYPE is defined to be int, etc.

ideas?

Upvotes: 1

Views: 1609

Answers (5)

Jim Balter
Jim Balter

Reputation: 16406

Preprocessor #if evaluates integer expressions. You're trying to treat == as if it were comparing tokens. Since int, char, double, etc. are not defined as preprocessor variables, they all evaluate as 0.

You could do

#define TYPE int
#define FMT "%d"

void printArray(TYPE *a, int size){
    for (int i=0; i<size; i++)
        printf(FMT " ", a[i]);

    printf("\n");
}

Much simpler, and it works.

If you want to only have to specify the TYPE, you could do something like

#define FMT_int "%d"
#define FMT_char "%c"
#define FMT_double "%f"
#define FMT PPCAT(FMT_, TYPE)

where PPCAT is defined in my answer to C/C++ Macro string concatenation

Upvotes: 6

Lefteris E
Lefteris E

Reputation: 2814

http://msdn.microsoft.com/en-us/library/ew2hz0yd(v=VS.80).aspx

The constant-expression is an integer constant expression with some additional restrictions.

It seems u can't compare strings. Try the following:

#define TYPE_int /* put this too */
#define TYPE int /* or char or double*/
void printArray(TYPE *a, int size){
    for (int i=0; i<size; i++){
#ifdef TYPE_int
        printf("%d ", a[i]);
#elif defined TYPE_char
        printf("%c ", a[i]);
#elif defined TYPE_double
        printf("%f ", a[i]);
#endif
    }
    printf("\n");
}

Upvotes: 0

The C and C++ preprocessor can only work with numbers (numeric literals, to be exact). In expressions, any words it doesn't recognize (after all macro expansion) are treated as a 0.

You'd need to do something like this:

#define TYPE_int 0
#define TYPE_char 1
#define TYPE_double 2

#define TYPE_USED TYPE_int

#if TYPE_USED == TYPE_int
typedef int TYPE;
#elif TYPE_USED == TYPE_char
typedef char TYPE;
#elif TYPE_USED == TYPE_double
typedef double TYPE;
#endif

void printArray(TYPE *a, int size){
    for (int i=0; i<size; i++){
#if TYPE_USED == TYPE_int
        printf("%d ", a[i]);
#elif TYPE_USED == TYPE_char
        printf("%c ", a[i]);
#elif TYPE_USED == TYPE_double
        printf("%f ", a[i]);
#endif
    }
    printf("\n");
}

You could use boost.preprocessor to do some metaprogramming magic with preprocessor loops instead of listing all the values manually.

Of course, the above code applies to C. If you're using C++, use a template instead of macro hacks and std::cout instead of printf().

Upvotes: 4

James Kanze
James Kanze

Reputation: 153909

Preprocessor evaluation is more or less like C++ evaluation. It is numeric (despite the fact that the preprocessor works with text). Any preprocessor symbol which remains after macro expansion is replaced by 0, and C++ keywords are still symbols in the preprocessor. (There is a special exception for the preprocessor symbol true, which expands to 1.) So in the end, all of your comparisons come out to 0 == 0, which is always true.

Upvotes: 1

Sean
Sean

Reputation: 62472

You should avoid the pre-processor wherever possible, and this is a classic example of when to avoid it! If you need to write code that depends on type then you can use either template, inheritance or polymorphism for this.

In this case you can rewrite printArray as a template function:

template<class T>
void printArray(T *data, int count)
{
  for(int i=0; i<count; i++)
  {
    cout << data[i] << " ";
  }

  cout << endl;
}

Upvotes: 1

Related Questions