检测预处理器宏中的空参数

时间:2011-10-31 12:35:29

标签: c macros c-preprocessor

我在vanilla 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,如何修改函数以打印所有局部变量? e.g。

    GLOG();
    

    可能会打印

    do_count:counter.c!123  count=456, message="Hello world", array=[7, 8] structure={ptr=0xACE0FBA5E, coord={x=9, y=0}}
    
  2. 如果不可能,我该怎样修改它才能打印当前的功能,文件和行号? e.g。

    do_count:counter.c!123
    

    原样,这会返回错误:

      

    错误:','标记

    之前的预期表达式

    因为strncat行只是

    strncat(format, , 248);
    

4 个答案:

答案 0 :(得分:4)

首先,由进程本身在运行时检查所有局部变量似乎是不可能的,因为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__,因为后者是非标准的。

答案 1 :(得分:2)

我在this回答中找到this个链接。它可能会帮助您解决问题的第一部分。

第二,如何获得所有局部变量,即使不是不可能,也要困难得多。原因是代码在编译时实际上没有变量,它只是偏移到内存区域(堆栈)。你的编译器可能有内部函数可以用来检查堆栈,但是那么你只有可能的值而不是变量的名称。我认为唯一的解决方案是使用特殊的预处理器宏来声明局部变量,然后是一个结构列表来表示内省,这将是很多运行时和内存开销。

答案 2 :(得分:1)

正如其他人提到的,C没有反射功能,因此您无法在宏调用中捕获局部变量。话虽这么说,如果你想要一个宏有条件地发生取决于宏调用是否有任何参数(即你的“非null”和“null”参数),那么你可以做类似的事情以下内容:

#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 */ } 

答案 3 :(得分:0)

根据Blagovest Buyukliev的回答,我为第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)以吞下尾随分号,以便以下if ... else有效:

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

(来自http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html的想法)。