Как я могу печатать значения в C без указания их типов, например, с помощью cerr << (A) << endl "в С++?
В С++, имея
#define DEBUG(A) cerr << (A) << endl;
Я могу отправить что-нибудь к нему, и он может его распечатать. Однако в C я должен указать его тип с% d,% c или% s и т.д. Но я не хочу писать его тип все время, я хочу использовать fprintf
как cerr
. Как я могу это сделать?
Например: в 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
Ответы
Ответ 1
Вы можете использовать Расширения языка GNU C:
#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"); \
})
Это нормально:
DEBUG("hello"); //prints hello
DEBUG(11110); //prints 11110
Но для символов вы должны использовать его с lvalue, иначе его тип будет "int":
char c='A';
DEBUG(c); // prints A
DEBUG('A'); // prints 65
Ответ 2
Вы не можете использовать fprintf()
так, как хотите. Добро пожаловать в C.
Операторы потока ввода-вывода С++ являются типичными и используют перегрузку оператора для достижения своей магии. Это не доступно в C, поэтому вы должны придерживаться небезопасных подстрок строк.
Ответ 3
В принципе вы не можете, так как C не имеет механизма перегрузки.
Однако вы можете определить несколько макросов, например:
#define DEBUG_INT(x) fprintf(stderr, "%d\n", (x))
#define DEBUG_CHAR(x) fprintf(stderr, "%c\n", (x))
Ответ 4
Невозможно избавиться от спецификаций преобразования, но если у вас есть компилятор C99, вы можете использовать __VA_ARGS__
и сделать его немного проще, например, в
#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;
}
Ответ 5
В C вы не можете надежно определить тип объекта. Следовательно, вам нужно будет ввести некоторый механизм поддержки общего программирования, например. охватывающий все объекты в структуре, которая содержит информацию о типе:
enum {type_int, type_double, type_string, /* ... */ } type_t;
struct {
type_t type;
void *obj;
} generic_type;
Теперь вы можете переключиться на ((generic_type)my_object).type
. Вероятно, это не то, что вы ищете.
Однако есть простой трюк, чтобы определить, является ли аргумент макроса строковым литералом или чем-то еще. С символом макрокоманды '#' вы можете включить макрос в строку:
#define DEBUG(x) if (#x[0] == '"') printf("%s\n", x); else printf("%d\n", x)
Теперь вы можете сделать следующее:
DEBUG("foo bar"); // prints "foo bar"
DEBUG(23); // prints "23"
В обратном направлении это не позволит вам различать, например, int
и float
s. Кроме того, указатели на char не распознаются как строки:
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
На некоторых машинах sizeof(double) != sizeof(int)
. Это может помочь провести различие между различными типами аргументов макроса, но это, конечно, не переносимо.
Вообще говоря, вы не сможете полностью решить эту проблему без каких-либо серьезных усилий по поддержке универсального программирования, а также сохраняя мобильность.
Мой совет: просто привыкнете к форматированию спецификаторов.
Ответ 6
Вместо использования __VA_ARGS__
вы можете просто сделать следующее:
#define DEBUG(x...) fprintf(stderr, x)
В отличие от встроенных методов GCC, этот метод позволяет вам смешивать и сопоставлять - вы можете печатать "string =" вместо одного типа за раз. Вы также не столкнетесь с несовместимостью по размеру и типу - это по сути то же самое, что и fprintf, кроме возможности быть исключенным во время сборки!