将__func__视为字符串文字而不是预定义的标识符

时间:2013-08-18 12:00:06

标签: c macros

我正在使用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__宏?

4 个答案:

答案 0 :(得分:20)

根据您的评论,目标是有一个宏,它将文件名和函数名称(可能是行号)组合成一个字符串,可以作为参数传递给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__不是宏。

相关问题What's the difference between __PRETTY_FUNCTION__, __FUNCTION__, __func__?涵盖了一些替代名称。这些是GCC特定的扩展,而不是标准名称。此外,GCC 4.8.1文档说:

  

这些标识符不是预处理器宏。在GCC 3.3及更早版本中,仅在C中,__FUNCTION____PRETTY_FUNCTION__被视为字符串文字;他们可以使用   初始化char数组,它们可以与其他字符串文字连接。 GCC   3.4及以后将它们视为变量,如__func__。在C ++中,__FUNCTION____PRETTY_FUNCTION__一直是变量。

有充分理由说明为什么这些不能成为预处理器构造。预处理器不知道函数是什么以及它正在处理的文本是否在函数的范围内,或者封闭函数的名称是什么。它是一个简单的文本处理器,而不是编译器。显然,可以在预处理器中建立这么多的理解(仅用于支持这一特性),但标准并不要求它,标准也不需要它。

不幸的是,我认为这意味着尝试将__func__(通过任何拼写)与__FILE____LINE__组合在一个宏中以生成单个字符串文字是注定的。

显然,您可以使用标准的两步宏机制将文件名和行号生成为字符串:

#define STR(x) #x
#define STRINGIFY(x) STR(x)

#define FILE_LINE __FILE__ ":" STRINGIFY(__LINE__)

但是,您无法将函数名称作为字符串文字的一部分获取。

有些论点认为文件名和行号足以确定问题所在;功能名称几乎是不必要的。它更具有外观,而不是功能,对程序员有一定帮助,但对其他用户没有帮助。

答案 1 :(得分:3)

经过快速实验后,我发现您无法使用__func__进行字符串化。如果可以的话,这没有多大意义,因为这意味着该值将是定义宏而不是应用宏的位置。

__func__的性质,如问题评论中所述,在this answer中有描述。

字符串化是在预处理器时执行的,因为__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;
}

答案 2 :(得分:3)

注意,“ __func__不是函数,因此无法调用;实际上,它是一个预定义的标识符,指向一个字符串,该字符串是函数的名称,并且仅有效在函数范围内。“ - Jonathan。

您正在寻找以下内容:

#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__。请参阅this

答案 3 :(得分:1)

这是一个语法错误。我尝试过你的宏规范,但我找不到有效的方法,所以也许你可以试试这个:

#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);
}