我正在使用 gcc 编译 C99 代码。我想编写一个宏,它将返回一个包含函数名称和行号的字符串。
这就是我所拥有的:
#define INFO_MSG __FILE__ ":"__func__"()"
但是,当我编译尝试使用此字符串的代码时,例如:
char buff[256] = {'\0'}
sprintf(buff, "Something bad happened here: %s, at line: %d", INFO_MSG, __LINE__);
printf("INFO: %s\n", buff);
我收到以下错误消息:
error: expected ‘)’ before ‘__func__’
我已经将问题追溯到宏观上。当我从宏中删除
__func__
时,代码可以正确编译。
如何修复宏,以便我可以在字符串中包含预定义的
__func__
宏?
从您的评论来看,目标是有一个宏,它将文件名和函数名(可能还有行号)组合成一个字符串,可以作为参数传递给函数,例如
printf()
或 strcpy()
或syslog()
。
不幸的是,我认为这是不可能的。
C11标准说:
ISO/IEC 9899:2011 §6.4.2.2 预定义标识符
¶1 标识符
应由翻译器隐式声明,就好像紧跟在每个函数定义的左大括号之后的声明__func__
static const char __func__[] = "function-name";
出现,其中 function-name 是词法封闭函数的名称。
因此,
__func__
不是宏,与 __FILE__
或 __LINE__
不同。
__PRETTY_FUNCTION__
、__FUNCTION__
、__func__
有什么区别?涵盖了一些替代名称。 这些是 GCC 特定的扩展,而不是标准名称。此外,GCC 4.8.1 文档说:
这些标识符不是预处理器宏。在 GCC 3.3 及更早版本中,仅在 C 中,
和__FUNCTION__
被视为字符串文字;它们可以被使用 初始化 char 数组,并且它们可以与其他字符串文字连接。海湾合作委员会 3.4 及更高版本将它们视为变量,如__PRETTY_FUNCTION__
。在 C++ 中,__func__
和__FUNCTION__
一直是变量。__PRETTY_FUNCTION__
有充分的理由说明为什么这些不能是预处理器构造。预处理器不知道函数是什么,也不知道它正在处理的文本是否在函数的范围内,或者封闭函数的名称是什么。 它是一个简单的文本处理器,而不是编译器。 显然,可以在预处理器中建立如此多的理解(仅是为了支持这一功能),但这不是标准所要求的,也不应该是标准所要求的。
但不幸的是,我认为这意味着尝试将
__func__
(任何拼写)与 __FILE__
和 __LINE__
组合在单个宏中以生成单个字符串文字是注定要失败的。
显然,您可以使用标准的两步宏机制将文件名和行号生成为字符串:
#define STR(x) #x
#define STRINGIFY(x) STR(x)
#define FILE_LINE __FILE__ ":" STRINGIFY(__LINE__)
但是,您无法将函数名称作为字符串文字的一部分放入其中。
有人认为文件名和行号足以确定问题所在;函数名称几乎没有必要。 它的装饰性多于功能性,对程序员有一点帮助,但对其他用户没有帮助。
经过快速实验,我发现您不能将
__func__
与字符串化一起使用。 如果可以,那就没有多大意义,因为这意味着该值将位于定义宏的位置,而不是应用宏的位置。
__func__
的本质,如问题评论中所述,在这个答案中进行了描述。
字符串化是在预处理器时执行的,因此
__func__
不可用,因为它本质上是稍后在编译过程中定义的函数本地字符串。
但是,只要不对其使用字符串化,就可以在宏中使用
__func__
。 我认为以下内容可以满足您的需求:
#include <stdio.h>
#define INFO_MSG "Something bad happened here: %s : %s(), at line: %d", \
__FILE__, __func__, __LINE__
int main()
{
char buff[256] = {'\0'};
sprintf(buff, INFO_MSG);
printf("INFO: %s\n", buff);
return 0;
}
请注意,在所提出的问题中,没有特别的理由使用字符串缓冲区。 以下
main
函数将达到相同的效果,而不会出现缓冲区溢出的可能性:
int main()
{
printf("INFO: ");
printf(INFO_MSG);
printf("\n");
return 0;
}
就我个人而言,我会像这样在宏中总结整个过程:
#include <stdio.h>
#define INFO_MSG(msg) printf("%s: %s : %s(), at line: %d\n", \
msg, __FILE__, __func__, __LINE__)
int main()
{
INFO_MSG("Something bad happened");
return 0;
}
注意,“
__func__
”不是一个函数,所以不能被调用;事实上,它是一个预定义的标识符,指向一个字符串,该字符串是函数的名称,并且仅在函数范围内有效.” - 乔纳森.
以下是您正在寻找的内容:
#define TO_STR_A( A ) #A
#define TO_STR( A ) TO_STR_A( A )
#define INFO_MSG TO_STR( __LINE__ ) ":" __FILE__
char buff[ 256 ] = { 0 };
sprintf( buff, "Something bad happened here, %s in function %s().", INFO_MSG, __func__ );
printf( "INFO: %s\n", buff );
...请注意,可以在函数本身内部调用
__func__
。请参阅这个。
这是一个语法错误。我尝试了你的宏规范,但我没有找到有效的方法,所以也许你可以尝试这个:
#define INFO_MSG __FILE__ , __FUNCTION__
int main()
{
char buff[256] = {'\0'};
sprintf(buff, "Something bad happened here: %s : %s(), at line: %d", INFO_MSG, __LINE__);
printf("INFO: %s\n", buff);
}