gpt4 book ai didi

c - 在宏中提取函数名称

转载 作者:太空狗 更新时间:2023-10-29 16:25:14 25 4
gpt4 key购买 nike

在C语言中,我们经常要运行这样的代码

if (! somefun(x, y, z)) {
perror("somefun")
}

是否可以创建一个宏,使用如下:

#define chkerr ...
chkerr(somefun(x, y, z));

会编译成上面的吗?

我已经知道我可以使用 __VA_ARGS__ 宏,但这需要我这样调用它

chkerr(somefun, x, y, z)         

最佳答案

短变体(您已经发现):

#define chkErr(FUNCTION, ...)  \
if(!FUNCTION(__VA_ARGS__)) \
{ \
perror(#FUNCTION); \
}

请注意,这可能会给嵌套的 if/else 或类似结构带来大问题:

if(x)
chkErr(f, 10, 12) //;
//^ semicolon forgotten!
else
chkErr(f, 12, 10);

将编译成等同于以下的代码:

if(x)
{
if(!f(10, 12))
perror("f");
else if(!f, 12, 10))
perror("f");
}

很明显这不是用宏编写的 if/else 的意图...所以你真的应该更愿意让它看起来像一个真正的函数(需要一个分号):

#define chkErr(FUNCTION, ...)      \
do \
{ \
if(!FUNCTION(__VA_ARGS__)) \
{ \
perror(#FUNCTION); \
} \
} \
while(0)

你可以这样调用它:

chkErr(someFunction, 10, 12);

如果出现错误,输出将是:

someFunction: <error text>

但是,这隐藏了函数实际被调用的事实,使得“局外人”更难理解。相同的输出,不隐藏函数调用,但在函数和参数之间需要一个额外的逗号(与普通函数调用相比):

#define chkErr(FUNCTION, ARGUMENTS) \
do \
{ \
if(!FUNCTION ARGUMENTS) \
{ \
perror(#FUNCTION); \
} \
} \
while(0)

chkErr(someFunction,(12, 10));
// ^ (!)

另一种具有保留函数调用魅力的变体将打印出整个函数调用:

#define chkErr(FUNCTION_CALL)   \
do \
{ \
if(!FUNCTION_CALL) \
{ \
perror(#FUNCTION_CALL); \
} \
} \
while(0)

chkErr(someFunction(10, 12));

如果出现错误,输出将是:

someFunction(10, 12): <error text>

附录:如果您真的想要完全问题中所示的输出并且仍然保留函数调用(中间没有逗号), 你有点麻烦。实际上,它可能的,但它需要一些额外的工作:

问题在于预处理器如何对宏参数进行操作:每个参数都是一个标记。它可以很容易地组合 token ,但不能拆分它们。

省略任何逗号会导致宏接受一个标记,就像在我的第二个变体中一样。当然,您可以像我一样将其字符串化,但是您会得到函数参数。这是一个字符串文字,由于预处理器无法修改字符串文字,您必须在运行时对它们进行操作。

接下来的问题是,字符串文字是不可修改的。所以你需要修改一个副本!

以下变体将为您完成所有这些工作:

#define chkErr(FUNCTION_CALL)                                 \
do \
{ \
if(!FUNCTION_CALL) \
{ \
char function_name[] = #FUNCTION_CALL; \
char* function_name_end = strchr(function_name, '('); \
if(function_name_end) \
*function_name_end = 0; \
perror(function_name); \
} \
} \
while(0)

好吧,你来决定是否值得付出努力......

顺便说一句——函数名和左括号之间的空格没有被消除。如果你想变得完美:

unsigned char* end = (unsigned char*) function_name;
while(*end && *end != '(' && !isspace(*end))
++end;
*end = 0;

或者,更好(感谢 chqrlie 的提示):

function_name[strcspn(function_name, "( \t")] = 0;

我能想到的任何其他方法都需要额外的预处理步骤:

#define CAT(X, Y) CAT_(X, Y)
#define CAT_(X, Y) X ## Y

#define chkErr(FUNCTION_CALL) \
do \
{ \
if(!FUNCTION_CALL) \
{ \
perror(CAT(CHK_ERR_TEXT_, __LINE__)); \
} \
} \
while 0

chkErr(function(10, 12));

啊,呵呵,这会导致这样的代码:

if(!function(10, 12))
{
perror(CHK_ERR_TEXT_42);
}

现在,从哪里获取这些宏?好吧,预处理,还记得吗?可能是 perl 或 python 脚本,e。 G。生成一个你必须包含的额外头文件。您必须确保每次在编译器的预处理器运行之前完成此预处理。

好吧,所有这些都不是不可能解决的,但我会把它留给我们中间的受虐狂……

关于c - 在宏中提取函数名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44942195/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com