Написание вариативного макроса, который использует имена переданных аргументов

Я хочу написать вариативный макрос, который каким-то образом знает имена переданных аргументов. Например:

Код:

int x = 2;
float f = 4.6;
char c = 'A';
char* str = "Bla bla";
PRINT("%d  %f %c  %s", x, f, c, str);     // calling the macro

должен производить вывод

x=2 f=4.6 c=A str=Bla bla.

Надеюсь, кто-то знает ответ на этот вопрос.


person user3115040    schedule 07.01.2014    source источник
comment
для char print iss %c а не %s я обновил вопрос с этим   -  person MOHAMED    schedule 07.01.2014
comment
Я почти уверен, что это невозможно сделать.   -  person JeremyP    schedule 07.01.2014
comment
К ОП: вы уверены, что хотите именно этого? Это не может быть легко сделано! (Но если вы потратите несколько дней или недель на работу, вы можете получить что-то близкое).   -  person Basile Starynkevitch    schedule 07.01.2014
comment
Какую проблему вы пытаетесь решить?   -  person Mr Lister    schedule 07.01.2014
comment
Это невозможно точно так же, как в вашем примере: строковый литерал, такой как "%d %f %c %s", не может быть выделен, дополнен в середине или изменен препроцессором C. Он может работать только для одиночных параметров, потому что конкатенация строк позволяет добавить другую строку перед строковым литералом.   -  person Jens    schedule 07.01.2014
comment
@Jens: Действительно. Конечно, автоматическая сборка строки формата (скажем, путем передачи формата вместе с аргументами или их вывода через дженерики типа C11), вероятно, будет проще в использовании в любом случае.   -  person doynax    schedule 07.01.2014


Ответы (6)


Близко, но не совсем (работает только для одиночного выражения), что требовалось автору запроса:

#define PRINT(fmt, var) printf(#var " = " fmt, (var))

Вот пример:

#include <stdio.h>
#include <stdlib.h>

#define PRINT(fmt, var) printf(#var " = " fmt, (var))

int
main(int argc, char *argv[])
{
    int x = 2, y = 3;
    float f = 4.6;
    char c = 'A';
    char *str = "Bla bla";
    PRINT("%d\n", x);
    PRINT("%f\n", f);
    PRINT("%c\n", c);
    PRINT("%s\n", str);
    PRINT("%d\n", x+y);
    exit(EXIT_SUCCESS);
}
person Lee Duhem    schedule 07.01.2014
comment
@Dipto Да, вы правы, поэтому я сказал «близко, но не совсем» :-) - person Lee Duhem; 07.01.2014
comment
Опечатка: не сигнал, а одиночная переменная! - person Basile Starynkevitch; 07.01.2014
comment
@leeduhem Не стесняйтесь откатывать. Похоже, мое редактирование стерло ваше. - person ; 07.01.2014
comment
@remyabel «сингл» в порядке. Но я думаю, что "одиночное выражение" более точно, этот макрос также работает для x+y. - person Lee Duhem; 07.01.2014

Я не думаю, что вы можете достичь того, чего хотите.

Но что-то вроде этого может быть вашим:

#define PRINT(fmt, val) fprintf(stderr, "%s=" fmt, #val, val)

...

int x = 2;
float f = 4.6;
char c = 'A';
char* str = "Bla bla";
// calling the macro:
PRINT("%d  ", x);
PRINT("%f ", f);
PRINT("%c  ", c);
PRINT("%s\n", str);
person glglgl    schedule 07.01.2014

Внимательно прочитайте документацию по cpp. В частности, об аргументах макросов, строчное определение, объединение и вариативные макросы. Там хорошо объясняют.

Возможно, вы не сможете добиться именно того, чего хотите, потому что вам нужно разделить строку формата.

Возможно, уменьшите свои цели (например, примите только один аргумент для PRINT, см. это или что отвечает) или рассмотрите возможность использования более мощного препроцессора, такого как GPP.

Вы также можете настроить GCC (добавив свои встроенные функции), например. MELT, но это, вероятно, не стоит недель усилий (для новичка), необходимых для этого.

person Basile Starynkevitch    schedule 07.01.2014

Немного того, что вы можете хотеть:

#include <stdio.h>

#define STRINGIFY(x) #x, (x)
#define FPRINTF(file, fmt, ...) fprintf(file, fmt, __VA_ARGS__)
#define PRINTF(fmt, ...) FPRINTF(stdout, fmt, __VA_ARGS__)

int main(void)
{
  int i = 42;
  char ch = 'a';
  char str[4] = "alk";

  PRINTF("%s=%s, %s=%c, %s=%d\n", 
    STRINGIFY(str), 
    STRINGIFY(ch), 
    STRINGIFY(i)
  );

  /* of just use printf directly: */
  printf("%s=%s, %s=%c, %s=%d\n", 
    STRINGIFY(str), 
    STRINGIFY(ch), 
    STRINGIFY(i)
  );

  return 0;
}
person alk    schedule 07.01.2014

/Надевает шляпу Индианы Джонса/

Я могу быть слишком поздно, но я здесь, чтобы сказать, что у этой проблемы действительно есть правильное решение.

Во-первых, некоторые предварительные условия определяют (объяснение здесь ):

#define L(c, ...) \
L4(c,1,0,,,,,,,,,,,,,##__VA_ARGS__) L4(c,0,1,,,,,,,,,##__VA_ARGS__) \
L4(c,0,2,,,,,        ##__VA_ARGS__) L4(c,0,3,        ##__VA_ARGS__)

#define L4(c, f, n, ...) \
L3(c,f,n##0,,,,__VA_ARGS__) L3(c,0,n##1,,,__VA_ARGS__) \
L3(c,0,n##2,,  __VA_ARGS__) L3(c,0,n##3,  __VA_ARGS__)

#define L3(...) L2(__VA_ARGS__, \
1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, )

#define L2(c, f, \
n00,n01,n02,n03, n04,n05,n06,n07, n08,n09,n0A,n0B, n0C,n0D,n0E,n0F, \
a00,a01,a02,a03, a04,a05,a06,a07, a08,a09,a0A,a0B, a0C,a0D,a0E,a0F, \
s, ...) L##s(c, f, n00, a00)

#define L1(c, f, n, a) c##f(n, a)
#define L0(c, f, n, a)

Затем код, который на самом деле является расширением ответа @alk:

#include <stdio.h>

#define STRING1(n, a) #a, (a)
#define STRING0(n, a) , STRING1(n, a)
#define PRINTF(fmt, ...) printf(fmt, L(STRING, __VA_ARGS__))

int main(int argc, char *argv[]) {
    int i = 42;
    char ch = 'a';
    char str[4] = "alk";
    /** every var must be preceded with '%s' for its name to be shown **/
    PRINTF("%s=%s, %s=%c, %s=%d\n", str, ch, i);
    return 0;
}

Эта версия подходит только для [0..16] аргументов, но ее можно легко распространить на любое количество аргументов, особенно на степень двойки. Однако чем больше аргументов она поддерживает, тем менее элегантно она выглядит.

P.S.: @BasileStarynkevitch уже предоставил все нужные ссылки, чтобы прояснить, как это работает.

person hidefromkgb    schedule 18.10.2016

Следующее работает для меня в gcc 4.7:

#include <stdio.h>
#define PRINT(...) fprintf (stderr, __VA_ARGS__)

int main()
{
int x = 2;
float f = 4.6;
char c = 'A';
char* str = "Bla bla";
PRINT("%d  %f %c  %s", x, f, c, str); // calling the macro
}

(обратите внимание, что я отредактировал вызов макроса, заменив %s на %c)

С Уважением

person Jesús Casado    schedule 07.01.2014
comment
Он также не отображает name x (только его значение), чего хочет OP. - person Basile Starynkevitch; 07.01.2014