Question

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;
}
Was it helpful?

Solution

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)

OTHER TIPS

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.

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.

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top