2

This started as a joke type of Cython, with me making a lot of silly defines to emulate python using C. Then I realized it was actually sort of convenient for debugging and quickly hacking together programs.

I have a define that mostly works, using sizeof to distinguish types, but a 3 or 7 character char array/string + \0 will be printed as a double or an int. Is there anyway around this? I was considering using a try-exception to subscript it to see if it's a string, but I can't implement it.

#include <stdio.h>

#define print(n) if(sizeof(n)==8)      printf("%f\n",n); \
                 else if(sizeof(n)==4) printf("%i\n",n); \
                 else                  printf("%s\n",n); 

int main()
{
    print("Hello world!") // these all work perfectly
    print(8.93)
    print(4)

    print("abc") // these are interpreted as ints or doubles
    print("1234567")

    return 0;
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Charles Clayton
  • 17,005
  • 11
  • 87
  • 120
  • 2
    Basing the print format on size only works for a very limited set of types, and the set of types for which it more or less works depends on the platform. For example, on a 64-bit platform, `long`, `double` and `void *` are all size 8 (Windows 64 is different, but the details are largely immaterial). In all cases, size cannot distinguish between the signed and unsigned integer types. – Jonathan Leffler Apr 28 '14 at 05:46
  • Very good point, but I'm not planning to implement this in any serious way, it's just a quick easy tool. I won't be using it on other computers. – Charles Clayton Apr 28 '14 at 05:51
  • The things you could do easily if you switched to C++ :) :) – R Sahu Apr 28 '14 at 06:06
  • @R Sahu These things were done in linux kernel for years, using a couple of handy gcc extensions. – oakad Apr 28 '14 at 06:07

4 Answers4

3

gcc has a handy built-in for you (also available with clang), which allows to directly compare types:

int __builtin_types_compatible_p (type1, type2)

This built-in function returns 1 if the unqualified versions of the types type1 and type2 (which are types, not expressions) are compatible, 0 otherwise. The result of this built-in function can be used in integer constant expressions.

http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

This built-in is used for type-specific dispatch and type checking in linux kernel, to name one example.

To get the types out of expressions, you can rely on typeof() statement, to the tune:

__builtin_types_compatible_p (typeof(n), int)
Community
  • 1
  • 1
oakad
  • 6,945
  • 1
  • 22
  • 31
  • A handy feature, indeed. – R Sahu Apr 28 '14 at 06:17
  • The other functions are neater looking, however they don't all work with GCC, whereas this can be implemented on nearly every compiler. – Charles Clayton Apr 28 '14 at 18:08
  • ...? You have that backwards: *this* is the "non-portable" GCC extension - the other answers provide a standard solution that works on every compliant C compiler, including GCC. (Of course if this one works better for you, then it's still the right choice, but portability isn't the reason.) – Alex Celeste Apr 29 '14 at 02:28
3

Selecting an operation based on the type of the argument can be done with the _Generic construct:

#define print(X) _Generic((0, X), int: print_int, \
                                  double: print_double, \
                                  char *: print_string)(X)

void print_int(int x) { printf("%d\n", x); }
// etc

All _Generic does is select one of the expressions in its list - that's why the argument to the macro appears twice (once to select based on its type, once to actually apply it to whichever function/value was chosen). You can also supply a default expression. Apparently the comma expression (that (0, X)) is necessary to easily support string literals: it decays the string into a char *.

_Generic is available in compilers that support C11 (which includes GCC and Clang). There's a workaround for some C99 compilers in Jens Gustedt's P99, as well.

Community
  • 1
  • 1
Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
  • 1
    you were some seconds faster than myself to answer :) and this is more complete. Only your example for `print_int` should use `x`, I think. – Jens Gustedt Apr 28 '14 at 06:34
  • @crclayton, you probably just don't have a compiler that is yet C11 or you'd have to enable C11 with some switch. Compilers to support this are clang (since some time) and gcc since the shiny new release 4.9. Otherwise you'd have to use P99 as indicated. – Jens Gustedt Apr 28 '14 at 07:58
3

Modern C, AKA C11, has _Generic for type generic macros. Something like

#define P(x) printf(_Generic(x, unsigned: "%u", signed: "%d", double: "%g"), x)

should do the trick.

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

The non-C++11 version perhaps, simply overloading a function... It's more than just a macro obviously:

void print_var(const char *s)
{
  printf("%s", s);
}

void print_var(unsigned int u)
{
  printf("%u", u);
}

void print_var(int d)
{
  printf("%d", d);
}

void print_var(double f)
{
  printf("%f", f);
}

#define print(v)    { print_var(v); \
                      printf("\n"); }


int main()
{
  print("abc")
  print(1234)
  print(-13)
  print(3.141592654)
  print("1234567")
  print("Hello world!")
  print(8.93)
  print(4)
  if(1)
    print("this too")
  if(2)
    print("and this")
}

Yielding the output:

abc
1234
-13
3.141593
1234567
Hello world!
8.930000
4
this too
and this
Martin G
  • 17,357
  • 9
  • 82
  • 98