Check if a macro argument is a pointer or not

邮差的信 提交于 2019-12-17 16:58:14

问题


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?


回答1:


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.




回答2:


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




回答3:


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


来源:https://stackoverflow.com/questions/19255148/check-if-a-macro-argument-is-a-pointer-or-not

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!