Inverse currying for macros?

自作多情 提交于 2020-07-08 12:59:50

问题


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:


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.



来源:https://stackoverflow.com/questions/51689584/inverse-currying-for-macros

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!