Обнаружение нулевого параметра в макросе препроцессора - PullRequest
2 голосов
/ 31 октября 2011

У меня есть следующая макро функция в ванили C :

#define GLOG(format_string, ...) { \
  const char *file = strrchr(__FILE__, '/'); \
  char format[256] = "%s:%s!%d\t"; \
  strncat(format, format_string, 248); \
  strcat(format, "\n"); \
  printf(format, __FUNCTION__, file ? file : __FILE__, __LINE__, ##__VA_ARGS__); \
}

, который позволяет напечатать отладочное сообщение, содержащее текущую функцию, номер файла и номер строки, например,

GLOG("count=%d", count);

может напечатать

do_count:counter.c!123  count=456
  1. Как я могу изменить функцию для печати всех локальных переменных, если вызывающая сторона не указывает format_string? например,

    GLOG();
    

    может напечатать

    do_count:counter.c!123  count=456, message="Hello world", array=[7, 8] structure={ptr=0xACE0FBA5E, coord={x=9, y=0}}
    
  2. Если это невозможно, как я могу изменить его для печати только текущей функции, файла и номера строки? например, * * 1 022

    do_count:counter.c!123
    

    Как есть, это возвращает ошибку:

    ошибка: ожидаемое выражение перед символом ‘,’

    как строка strncat просто

    strncat(format, , 248);
    

Ответы [ 4 ]

4 голосов
/ 31 октября 2011

Во-первых, проверка всех локальных переменных во время выполнения самим процессом кажется невозможной, поскольку у C нет никаких средств для отражения.

Во-вторых, вам было бы намного лучше, если бы вы написали макрос регистрации так:

#include <stdio.h>

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)

#define GLOGF(fmt, ...) \
  printf("%s:%s " fmt "\n", __func__, __FILE__ "!" TOSTRING(__LINE__), ##__VA_ARGS__)

int main (void) {
  /* main:test.c!xx count=5 */
  GLOGF("count=%d", 5);
  /* main:test.c!xx */
  GLOGF();
  return 0;
}

Это проще и не приводит к дополнительным накладным расходам времени выполнения, поскольку строка сцепляется во время компиляции.

Также обратите внимание, что я использовал __func__ вместо __FUNCTION__, потому что последний нестандартный.

2 голосов
/ 31 октября 2011

Я нашел эту ссылку в этом ответе. Это может помочь вам с первой частью вопроса.

Второй способ получения всех локальных переменных гораздо сложнее, если не невозможен. Причина в том, что код, когда он скомпилирован, на самом деле не имеет переменных, он просто имеет смещения в области памяти (стеке). Возможно, ваш компилятор имеет внутренние функции, которые можно использовать для проверки стека, но тогда у вас есть только возможные значения, а не имена переменных. Единственное решение, которое я вижу, состоит в том, чтобы использовать специальные макросы препроцессора для объявления локальных переменных, а затем список структур, представляющих их для самоанализа, что потребует большого количества времени выполнения и нагрузки на память.

1 голос
/ 31 октября 2011

Как уже упоминали другие, C не имеет функций отражения, и поэтому вы не сможете захватывать локальные переменные в вызове макроса.При этом, если вы хотите, чтобы что-то происходило с макросом условно, в зависимости от того, есть или нет какие-либо аргументы для вызова макроса (т. Е. Ваши «ненулевые» и «нулевые» аргументы), то вы можете сделать что-то вродеследующее:

#include <string.h>

#define NULL_IDENT ""
#define IDENT(ident_name) #ident_name
#define MACRO(ident_name) \
    if (strcmp(NULL_IDENT, IDENT(ident_name)) == 0) { \
       /* add code for a null argument passed to the macro */ } \
    else { \
       /* add code for a non-null argument passed to the macro */ } 
0 голосов
/ 01 ноября 2011

Основываясь на ответе Благовеста Буюклиева, для части 2 я придумал следующее решение:

#define GLOG(fmt, ...) do { const char *fn = strrchr(__FILE__, '/');           \
  printf("%s:%s!%d\t"fmt"\n",__func__,fn?++fn:__FILE__,__LINE__,##__VA_ARGS__);\
 } while(0)

с использованием конкатенации строки препроцессора для простой конкатенации пустой строки, если параметр не указан.

Кроме того, я добавил do {...} while (0), чтобы проглотить конечную точку с запятой, чтобы сработало следующее, если ... else:

if (...)
 GLOG();
else     
 /* do something else */

(идея из http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html).

...