Question

Is there some "nice" way to check if a variable passed to a macro is a pointer? e.g.

#define IS_PTR(x) something
int a;
#if IS_PTR(a)
printf("a pointer we have\n");
#else
printf("not a pointer we have\n");
#endif

The idea is that this is not done run-time but compile-time, as in: we get different code depending on if the variable is a pointer or not. So I would like IS_PTR() to evaluate to some kind of constant expression in some way. Am I going about this idea all the wrong way?

Was it helpful?

Solution

It is certainly not observable through the preprocessor in #if as you imply in your question. The preprocessor knows nothing about types, only tokens and expressions that are constructed from them.

C11 has a new feature that lets you observe a particular pointer type, but not "pointerness" in general. E.g you could do something

#define IS_TOTOP(X) _Generic((X), default: 0, struct toto*: 1)

or if you'd want that the macro also works for arrays

#define IS_TOTOPA(X) _Generic((X)+0, default: 0, struct toto*: 1)

There are already some compilers around that implement this, namely clang, and for gcc and others you can already emulate that feature with some builtins, see P99.

OTHER TIPS

NULL is pretty much the only thing you can look for. There is no way to determine if something is a pointer.

I found a more or less _Generic solution of this problem.

Warning: May be triggered false-positive (see an example below).

#define __INTERNAL_CHECK_POINTER(x) _Generic((x),\
          int: 0,       unsigned int: 0,\
         long: 0,      unsigned long: 0,\
    long long: 0, unsigned long long: 0,\
        float: 0,             double: 0,\
  long double: 0,                       \
      default: 1)

/**
 * Determines whether the variable has likely a pointer type (but may be triggered false-positive)
 */
#define IS_LIKELY_A_POINTER(x) ((sizeof(x) == sizeof(void*)) && __INTERNAL_CHECK_POINTER(x) ? 1 : 0)

Demo:

char c = 0;
printf("c is a pointer: %s\n", IS_LIKELY_A_POINTER(c) ? "Yes" : "No");

unsigned long long l = 0;
printf("l is a pointer: %s\n", IS_LIKELY_A_POINTER(l) ? "Yes" : "No");

double d = 0.0;
printf("d is a pointer: %s\n", IS_LIKELY_A_POINTER(d) ? "Yes" : "No");

unsigned char* cp = 0;
printf("cp is a pointer: %s\n", IS_LIKELY_A_POINTER(cp) ? "Yes" : "No");

struct tm* tp = 0;
printf("tp is a pointer: %s\n", IS_LIKELY_A_POINTER(tp) ? "Yes" : "No");

char ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
printf("ia is a pointer: %s\n", IS_LIKELY_A_POINTER(ia) ? "Yes" : "No");

This will output:

c is a pointer: No
l is a pointer: No
d is a pointer: No
cp is a pointer: Yes
tp is a pointer: Yes
ia is a pointer: Yes // false-positive!

If you (like me) are looking for some logging (to draw or not to draw a * for a particular variable) and you're not looking for fail-proof results, try this, it may help. Cheers!

NOTE that it won't compile under MSVC; use gcc/clang/etc. instead or make your own fallback implementation using this condition:

#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
    // use _Generic code
#else
    // ¯\_(ツ)_/¯
#endif

On Clang and GCC, __builtin_classify_type(P) evaluates to 5 if P is an object with a pointer type.

There is a way without false positives nor negatives, but compilation will break if the parameter is neither an array nor a pointer.

#define is_same_type(a, b)    __builtin_types_compatible_p(typeof(a), typeof(b))
/* Is a an array? */
#define is_array(a)             (!is_same_type((a), &(a)[0]))
/* Is p a pointer? */
#define is_pointer(p)           (!is_array(p))

It needs the GCC builtin __builtin_types_compatible_p().

Behaviour:

If p is an array, it will evaluate as 0;

Else if p is a pointer, it will evaluate as 1;

Else, compilation will break.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top