逆逆向宏化? [英] Inverse currying for macros?

查看:52
本文介绍了逆逆向宏化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们有一个 #define FOO(x,y)东西.

我想构造这样的宏 #define BAR ,以使 BAR(x)(y)会调用 FOO(x,y).如果有可能,我该怎么办?


我尝试了以下操作:

  #define BAR(x)FOO(x,BAR_#定义BAR_(y)y) 

但是得到了:

 错误:调用宏"FOO"的未终止参数列表 

这是MCVE:

  #include< iostream>#define FOO(x,y)std :: cout<<x<<y<<'\ n';#定义BAR(x)FOO(x,BAR0#定义BAR0(y)y)#定义STR(...)STR0(__ VA_ARGS__)#定义STR0(...)#__VA_ARGS__int main(){std :: cout<<STR(BAR(1)(2));} 


另一种尝试:

  #define BAR FOO BAR0#定义BAR0(x)(x,BAR1#定义BAR1(y)y) 

该代码可以编译,但最终不会扩展 FOO(1、2).

MCVE:

  #include< iostream>#define FOO(x,y)std :: cout<<x<<y<<'\ n';#define BAR FOO BAR0#定义BAR0(x)(x,BAR1#定义BAR1(y)y)#定义STR(...)STR0(__ VA_ARGS__)#定义STR0(...)#__VA_ARGS__int main(){//打印`FOO(1、2)`,但我要`std :: cout<<1<<2<<'\ n';`std :: cout<<STR(BAR(1)(2));} 

解决方案

1.剖析错误

让我们从这里开始:

  #define BAR(x)FOO(x,BAR_#定义BAR_(y)y)#定义FOO(x,y)foo {x | y}酒吧(1)(2) 

请注意,我只打算使用预处理器来调试预处理器(为什么我只需要调用预处理器,为什么我要构建C ++程序,无论如何都要调用预处理器?(这是夸张的o/c;我只是在这里讲一个故事).

以下是CPP对此的看法.我们看到 BAR ;这是一个类似于函数的宏.有趣的是,除非我们看到一个圆括号,否则它是不可行的.接下来,我们看到...一个开放的括号.现在我们到了某处……我们需要确定它的论点.因此,我们继续扫描: BAR(1) ...那里...就是匹配的开放括号...好像我们在调用一个参数. BAR 原来是用一个参数定义的,所以效果很好.

现在我们执行参数替换 ...,因此我们注意到 BAR 的替换列表在非串化,非粘贴方式.这意味着我们应该评估相应的参数( 1 ),这很简单...仅仅是它本身.然后,该求值结果将替换替换列表中的参数,因此我们有了 FOO(1,BAR _ .我们现在完成了参数替换.

我们下一步要做的是重新扫描并进一步替换.因此,我们重新扫描... FOO ,是的.那是一个类似于函数的宏... FOO( ...并且正在被调用.现在我们到达某个地方...我们需要确定其参数.因此,我们继续扫描: FOO(1,BAR_(2) ...,然后突然我们到达文件末尾,是吗?那不对.正在调用 FOO ;应该有一个匹配的括号.

您可能天真地认为应该调用 BAR_(2),但这不是宏的工作方式.他们评估外在",而"in"(也称为参数标记)评估替换列表中是否提到该参数,而该提及不是字符串化或粘贴.>

请注意,如果 FOO 不是一个类似于函数的宏,那么这个故事将朝着完全不同的方向发展.在这种情况下, FOO(只是令牌,预处理器不会在意...所以当它看到 BAR_(2)时,它是调用宏,但是还有另一个骗子";如果在没有实际调用宏 FOO 的情况下传递了 FOO ,则令牌也会在这两种情况下,最终都会生成 FOO(1,2),这正是您想要的,但是如果您想评估 FOO 作为类似于函数的宏,您只有一个选择;您需要第二遍;第一遍实际上允许调用序列中的第二个参数来构建宏,而此遍历一定不能允许调用 FOO .需要 second 遍来调用它.

2.怎么做

好吧,这很简单:

  #define DELAY()#定义BAR(x)FOO DELAY()(x,BAR_#定义BAR_(y)y)#定义FOO(x,y)foo {x | y}#定义EVAL(...)__ VA_ARGS__酒吧(1)(2)评估(BAR(1)(2)) 

这是不同的地方(第一行).在替换 BAR 的参数之后,其替换列表现在为 FOO DELAY()(1,BAR _ ,而不仅仅是 FOO(1,BAR _ .现在,在重新扫描期间,它仍然会看到 FOO ,这仍然很有趣...但是它看到的下一个标记是 DELAY ,而不是一个开放的括号.此通行证认为是 not 调用了 FOO 并继续传递.在完整的 DELAY()展开后,什么也没产生,然后看到(1,BAR _ ;前三个只是令牌. BAR _ 是宏调用,所以故事也是如此...因此扩展BAR(1)(2)用于产生令牌 FOO(1,2),没有错误,但是不会评估 FOO .

但是 EVAL 接受 BAR(1)(2)作为参数.它的替换列表提到了它的参数"(可变arg变体),因此对 BAR(1)(2)进行了全面评估,生成了 FOO(1、2).然后重新扫描 FOO(1,2),这是实际调用 FOO 的时间.

Assume we have a #define FOO(x,y) something.

I want to construct such macro #define BAR that BAR(x)(y) would call FOO(x,y). How can I do it, if it's possible at all?


I tried following:

#define BAR(x) FOO(x, BAR_
#define BAR_(y) y)

But got:

error: unterminated argument list invoking macro "FOO"

Here's a MCVE:

#include <iostream>

#define FOO(x,y) std::cout << x << y << '\n';

#define BAR(x) FOO(x, BAR0
#define BAR0(y) y)

#define STR(...) STR0(__VA_ARGS__)
#define STR0(...) #__VA_ARGS__

int main()
{
    std::cout << STR( BAR(1)(2) );
}


Another attempt:

#define BAR FOO BAR0
#define BAR0(x) (x, BAR1
#define BAR1(y) y)

This one compiles, but leaves FOO (1, 2) unexpanded in the end.

MCVE:

#include <iostream>

#define FOO(x,y) std::cout << x << y << '\n';

#define BAR FOO BAR0
#define BAR0(x) (x, BAR1
#define BAR1(y) y)

#define STR(...) STR0(__VA_ARGS__)
#define STR0(...) #__VA_ARGS__

int main()
{
    // Prints `FOO (1, 2)`, but I want `std::cout << 1 << 2 << '\n';`
    std::cout << STR( BAR(1)(2) );
}

解决方案

1. Dissecting the error

Let's start here:

#define BAR(x) FOO(x, BAR_
#define BAR_(y) y)
#define FOO(x,y) foo{x|y}
BAR(1)(2)

Note that I'm only going to use the preprocessor to debug the preprocessor (why do I need to build C++ programs, which invokes the preprocessor anyway, when I can simply just invoke the preprocessor? (That's rhetorical o/c; I'm just telling a story here)).

Here's how the CPP looks at this. We see BAR; that's a function-like macro. Interesting, but not actionable unless we see an open parentheses. Next, we see... an open parentheses. Now we're getting somewhere... we need to identify its arguments. So we keep scanning: BAR(1)... there... that's the matching open parentheses... looks like we're invoking with one argument. BAR it turns out is defined with one argument, so this works great.

Now we perform argument substitution... so we note that BAR's replacement list mentions its parameter (x) in a non-stringifying, non-pasting way. That means we should evaluate the corresponding argument (1), which is easy... that's just itself. That evaluation result then replaces the parameter in the replacement list, so we have FOO(1, BAR_. We're now done with argument substitution.

The next thing we need to do is rescan and further replacement. So we rescan... FOO, ah, yes. That's a function-like macro... FOO( ...and it's being invoked. Now we're getting somewhere... we need to identify its arguments. So, we keep scanning: FOO(1, BAR_(2)...and, suddenly we reach the end of the file. Huh? That's not right. FOO is being invoked; it's supposed to have a matching parentheses.

You might naively think that BAR_(2) should be invoked, but that's not how macros work. They evaluate outer-in, and the "in" (aka, argument tokens) only evaluates if there's a mention of the parameter in the replacement list, where said mention is not a stringification or paste.

Note that if FOO were not a function-like macro, this story would go an entirely different direction. In such a case, FOO( would simply be tokens the preprocessor doesn't care about... so by the time it sees BAR_(2), it will be invoking a macro. But there's another "cheat"; if FOO is passed over without actually invoking the macro FOO, the tokens would also be skipped over. In both cases, FOO(1, 2) will eventually be produced, which is what you want. But if you then want to evaluate FOO as a function-like macro, you have only one choice. You need a second pass; first pass actually allows your second argument in the sequence to be invoked to build your macro, and this pass must not allow FOO to be invoked. The second pass is needed to invoke it.

2. How to do this

Well, that's easy enough:

#define DELAY()
#define BAR(x) FOO DELAY() (x, BAR_
#define BAR_(y) y)
#define FOO(x,y) foo{x|y}
#define EVAL(...) __VA_ARGS__
BAR(1)(2)
EVAL(BAR(1)(2))

Here's how this is different (first line). After BAR's argument substitution, its replacement list is now FOO DELAY() (1, BAR_ instead of just FOO(1, BAR_. Now, during the rescan, it still sees FOO, which is still interesting... but the next token it sees is DELAY, not an open parentheses. So the CPP in this pass decides it's not invoking FOO and passes on it. After the full DELAY() expansion, which produces nothing, it then just sees (1, BAR_; the first three are just tokens. BAR_, however, is a macro invocation, and so goes the story... so the result of expanding BAR(1)(2) is to produce the tokens FOO(1, 2), error free. But that doesn't evaluate FOO.

The EVAL, however, accepts BAR(1)(2) as an argument. Its replacement list mentions its "parameter" (varying arg variant), so BAR(1)(2) is fully evaluated, producing FOO(1, 2). Then FOO(1, 2) is rescanned, which is when FOO actually gets invoked.

这篇关于逆逆向宏化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆