简体   繁体   中英

How can I print values in C without specifying their types, like done with “cerr << (A) << endl” in C++?

In C++, having

#define DEBUG(A) cerr << (A) << endl;

I can send anything to it, and it can print it. However, in C, I must specify its type with %d, %c or %s etc. But I don't want to write its type all time, I want to use fprintf like cerr . How can I do that?

For example: in C

#define DEBUG(A) X // X is what I want to write 
...
// in function, when I put
DEBUG(5);          // I just want to print 5 
// or, with same statement, when I say 
DEBUG('a');        // output : a

You can use GNU C Language Extensions :

#define DEBUG(x)                                                 \
  ({                                                             \
    if (__builtin_types_compatible_p (typeof (x), int))          \
        fprintf(stderr,"%d\n",x);                                \
    else if (__builtin_types_compatible_p (typeof (x), char))    \
        fprintf(stderr,"%c\n",x);                                \
    else if (__builtin_types_compatible_p (typeof (x), char[]))  \
        fprintf(stderr,"%s\n",x);                                \
    else                                                         \
        fprintf(stderr,"unknown type\n");                        \

  })

These are fine:

DEBUG("hello"); //prints hello
DEBUG(11110);   //prints 11110 

But for chars, you should use it with lvalue, otherwise its type will be "int" :

char c='A';
DEBUG(c);    // prints A
DEBUG('A');  // prints 65

You can't use fprintf() in the way you want. Welcome to C.

C++ I/O stream operators are typesafe and use operator overloading to achieve their magic. That's not available in C so you have to stick to unsafe format string approaches.

In principle, you can't, as C don't have an overloading mechanism.

You can, however, define a number of macros like:

#define DEBUG_INT(x)  fprintf(stderr, "%d\n", (x))
#define DEBUG_CHAR(x) fprintf(stderr, "%c\n", (x))

There is no way to get rid of the conversion specifications, but if you have a C99 compiler, you can use __VA_ARGS__ and make it a little easier, as, for example, in

#include <stdio.h>

#define DEBUG(fmt, ...) fprintf(stderr, (fmt), __VA_ARGS__)

int main(void) {
  int foo = 42;
  char *t = "foobar";
  DEBUG("%s:%d\n", t, foo);
  return 0;
}

In C, you cannot reliably determine the type of an object. Hence, you'd have to introduce some mechanism to support generic programming, eg enclosing all objects in a structure which holds type information:

enum {type_int, type_double, type_string, /* ... */ } type_t;
struct {
        type_t type;
        void *obj;
} generic_type;

Now, you can switch over ((generic_type)my_object).type . This is probably not what you're looking for.



However, there's a simple trick to tell whether a macro argument is a string literal or something else. With the macro quoting character '#', you can turn a macro argument into a string:

#define DEBUG(x) if (#x[0] == '"') printf("%s\n", x); else printf("%d\n", x)

Now you can do the following:

DEBUG("foo bar");  // prints "foo bar"
DEBUG(23);         // prints "23"

On the downside, this won't let you distinguish between eg int s and float s. Furthermore, pointers-to-char are not recognized as strings:

char *foo = "bar";
DEBUG(foo);        // prints the value of foo, not the string pointed to by foo

double salary;
DEBUG(salary);     // prints (int)salary, not (double)salary

On some machines, sizeof(double) != sizeof(int) . This might help to further distinguish between the different types of your macro arguments, but it's certainly not portable.



Generally speaking, you won't be able to completely solve this problem without some serious effort towards generic programming support while also maintaining portability.

My advice: simply get used to format specifiers.

Instead of using the __VA_ARGS__ , you can just simply do the following:

#define DEBUG(x...) fprintf(stderr, x)

Unlike the GCC built-in methods, this method allows you to mix-and-match - you can print "string=", instead of just one type at a time. You also won't run into size and type incompatibility this way - it's essentially the same as fprintf, except able to be excluded at build-time!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM