问题 c预处理器将多个参数作为一个传递


C预处理器传递多个参数,就好像它们是一个参数一样。我觉得问题是我们如何调用它 untouchable 宏,但我们所做的每一次尝试都改变了 first 宏未能产生预期的结果。这是一个包含注释的完整代码示例,用于解释发生了什么以及我们想要发生什么:

//this can be changed, but it must remain a #define to be of any use to us
#define first a,b,c

// v all code below this line cannot be altered (it's outside of our control)

#define untouchable(name, c1, c2, c3) \
    wchar_t name[] = \
    { \
        quote(c1), \
        quote(c2), \
        quote(c3) \
    }

#define quote(c) L#@c

// ^ all code above this line cannot be altered (it's outside of our control)

int _tmain(int argc, _TCHAR* argv[])
{
    static untouchable(mrNess, first);

    // the line above is precompiled to:
    // static wchar_t mrNess[] = { L'a,b,c', L, L };

    // whereas we want:
    // static wchar_t mrNess[] = { L'a', L'b', L'c' };

    return 0;
}

我们正在VisualStudio中的Windows中进行编译。


9173
2017-07-12 19:45


起源

是什么 @ 做的 L#@c? - Edgar Rokjān
它用单引号括起来,而不是双引号。这是微软特有的。 - alice


答案:


当预处理器遇到类函数宏的调用时,它首先识别参数,然后完全宏扩展它们*,最后用替换列表替换宏调用,并在适当的位置插入扩展参数。宏观的扩张 first 是(或将会)执行得太迟,以便预处理器在扩展中识别宏的单独参数 untouchable()。你需要找到另一种方式。

一种可能性是插入一个间接级别:

#define untouch_wrap(name, args) untouchable(name, args)

static untouch_wrap(mrNess, first);

那里, first 以前扩大了 untouch_wrap,以便在重新扫描结果时进一步替换宏,从而产生调用 untouchable() 具有正确数量的参数。

这确实依赖于第二个论点 untouch_wrap() 扩展到以逗号分隔的正好三个成员的列表。定义更清晰,更灵活 untouch_wrap() 喜欢这个:

#define untouch_wrap(name, ...) untouchable(name, __VA_ARGS__)

...但是MSVC对可变参数宏的处理是不一致的,我怀疑它会超过它。


* 对于作为预处理程序字符串化操作数的参数,扩展被抑制(#)和令牌粘贴(##)运营商。


5
2017-07-12 20:22





问题似乎是你如何定义 first。你过关了 first 到了 untouchable 宏, 但那时显然没有扩大

所以不要使用 first 像这样的宏并通过 ab,和 c 作为宏的单个参数。然后你会得到你想要的。

更新

但是有一些希望:

#define UT_HELPER(a,b,c,d) untouchable(a,b,c,d)
#define UT(x, y) UT_HELPER(x, y)
#define first a, b, c
static UT(mrNess, first);

它类似于在中找到的解决方案 这个堆栈溢出的答案所以我不能声称自己已经考虑过了。

注意

#define quote(s) L#@s

在我的编译器中不起作用,所以我无法完全测试它。我只是试着用

#define quote(s) L###s

那很有效。

我希望这能让你选择真正的,更复杂的宏。


4
2017-07-12 19:59



如果你的意思是使用它 untouchable(mrNess, a, b, c),这正是我们做不到的。我们需要使用一个 #define 在通话中。 - alice
你是什​​么意思,你不能这样做?是什么阻碍了你? - Rudy Velthuis
我们正在重构一个大型代码库,其中一部分是使这些宏更易于理解。如果我们使用 a, b, c 它比使用更复杂 first (实际上我在这里简化了它,实际上有13个参数,所以它会是 a, b, c, d, e, f, ..., m 而不是简单的 first)。 - alice


C11,6.10.3.1论据替代:

在调用类似函数的宏的参数之后   已经确定,论证替代发生了。一个参数   替换列表,除非前面有#或##预处理令牌或   后跟一个##预处理令牌(见下文),替换为   在其中包含的所有宏之后的相应参数   扩大。在被替换之前,每个参数的预处理   令牌完全被宏观取代,好像它们形成了其余部分   预处理文件;没有其他预处理令牌可用。

看看它是如何扩展的:

#define first a,b,c
#define untouchable(name, c1, c2, c3) \
    wchar_t name[] = \
    { \
        quote(c1), \
        quote(c2), \
        quote(c3) \
    }

untouchable(foo,first,c,d)

成:

$ gcc -E foo.c
...

wchar_t foo[] = { quote(a,b,c), quote(c), quote(d) }  

首先识别参数,因此不可触摸的参数首先变为c1。只有在识别它们之后,它们才会被扩展,然后被替换为宏体。 first -> c1 -> a,b,c。现在在宏体中c1被替换为 a,b,c

我从问题中跳过了引用宏,因为它在我的编译器上是非法的。


1
2017-07-12 20:12



这让我在阅读文档时感到困惑。所有这些替换和扩展!如果我正在阅读你所写的内容,你是否正在说这一点 a,b,c 从 first 到了 untouchable 它们是不可改变的单一标记? - alice
是。如果你试试 untouchable(foo,first) 你得到一个错误,因为宏只看到两个参数,但期望4。 - evaitl