如何在C语言的宏中多次调用一个函数?

3
在我的开发环境中,一些ioctl调用第一次会失败,如果我再次调用同样的ioctl,则返回成功。这是由于一些与硬件相关的时间同步问题。
因此,我们使用一些临时解决方案,如用一个宏来遮蔽所有ioctl,在该宏内部调用ioctl三次。
但是,该宏会引发编译错误。请提供解决此问题的建议。
示例代码:
#include <stdio.h>

int func (int a, int b, int c)
{
    return -1;
}

#define IOCTL_WRAPPER(a,b,c)                    \
            {                                   \
                int i = 0, retval = 0;          \
                while (i < 3)                   \
                {                               \
                    retval = func (a, b, c);    \
                    if (retval != -1)           \
                    {                           \
                        break;                  \
                    }                           \
                    i++;                        \
                }                               \
                retval;                         \
            }

int main ()
{
    int RetVal = 0;

    RetVal = IOCTL_WRAPPER(1, 2, 3);

    if (RetVal != -1)
    {
        printf ("\n pass \n");
    }
    else
    {
        printf ("\n fail \n");
    }

    return 0;
}

编译错误

a.c: In function âmainâ:
a.c:9:13: error: expected expression before â{â token
             {                                   \
             ^
a.c:27:14: note: in expansion of macro âIOCTL_WRAPPERâ
     RetVal = IOCTL_WRAPPER(1, 2, 3);
          ^

我们不能使用函数来实现这个目的,因为ioctl调用的同时访问可能会导致问题。 - Hakkim Ansari
3
"Hakkim,那条评论没有意义。如果使用函数可能导致同时访问ioctl调用,那么宏也可以。语句只能在函数中执行。" - Peter
3
@HakkimAnsari你为什么需要宏? 为什么不能将其包装在普通函数中? 你的评论真的没有意义。 对我来说,这看起来像是一个XY问题。 - Jabberwocky
1
你的 IOCTL_WRAPPER 宏实际上包装的是 func 而不是 ioctl,这有点奇怪。这是在创建问题时改变了想法还是表明你有一个指向函数的指针,用于调用 ioctl?问题可能是多个线程修改函数指针引起的吗? - Jonathan Leffler
2
硬件相关的时间同步问题 - 调解安全并发访问设备的责任在ioctl()后面的操作系统代码中。您应该为在那里解决此问题提出论据。是的,我理解这可能超出了您的职责范围,但那是提供修复的最合适的地方。 - Brian Cain
2个回答

4

我建议使用do-while宏风格以避免将来出现的问题。为了解决你的问题,请将retVal作为参数传递:

#define IOCTL_WRAPPER(retval, a,b,c)        \
        do {                                \
            int i = 0;                      \
            while (i < 3)                   \
            {                               \
                retval = func (a, b, c);    \
                if (retval != -1)           \
                {                           \
                    break;                  \
                }                           \
                i++;                        \
            }                               \
        } while(0)

您的呼叫将会是:

IOCTL_WRAPPER(RetVal, 1, 2, 3);

2

您的宏展开为一个代码块,而不是一个表达式

为什么不使用内联函数?示例代码:

static inline int ioctl_wrapper(int a, int b, int c)
{
    int i = 0, retval = 0;
    while (i < 3)
    {
        retval = func (a, b, c);
        if (retval != -1)
        {
            break;
        }
        i++;
    }
    return retval;
}

尽管ioctl_wrapper()不必内联,但通常认为最好将优化工作留给编译器(特别是现代编译器)。如果编译器认为ioctl_wrapper()不应该内联,则使用宏来“强制内联”可能不是一个好主意。


我突然想到还有另一种非常简单的方法:

retval = (func(a, b, c) + 1) || (func(a, b, c) + 1) || (func(a, b, c) + 1);

如果三个调用中的任意一个返回值不是-1,retval将为1;否则,它将为0。

1
为什么要使用 extern 而不是 static?我会在内联函数中使用 static 而不是 extern - Jonathan Leffler
@JonathanLeffler 看起来 externstatic 都可以。参见:https://dev59.com/RXVC5IYBdhLWcg3wrDNd#216546 - nalzok
语法上讲,是的。你理解 extern 的语义了吗?这并不是很简单。 - Jonathan Leffler
@JonathanLeffler 我不确定...事实上,我从未编写过分布在多个文件中的C代码。也许您可以为我提供一些阅读资源? - nalzok
你已经得到了我提供的交叉引用(你已经链接到它),或者可能有另一个类似主题的问题。你不必改变你的答案,但如果你在inline中使用static,你的生活可能会更简单——你不必担心多个(可见)定义破坏你的链接。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接