如何在嵌套上下文中通过 __VA_OPT__ 扩展递归宏

2023-11-29

我读过了本文,这说明了如何__VA_OPT__函数宏可用于递归扩展宏。我想实现类似的东西,不同之处在于宏是在嵌套上下文中扩展的。

输入:

NEST_RECURSIVE(A, B, C)

应该产生(顺序无关):

((( | C) | B) | A)

我的方法稍微概括了这篇文章:

#define PARENS ()

#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__

#define FOR_EACH_R(func, ...) __VA_OPT__(EXPAND(FOR_EACH_HELPER_R(func, __VA_ARGS__)))
#define FOR_EACH_HELPER_R(func, sub, ...) func(__VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, __VA_ARGS__)), sub)
#define FOR_EACH_AGAIN_R() FOR_EACH_HELPER_R

#define MY_FUNC(nested, var) (nested | var)
#define NEST_RECURSIVE(...) FOR_EACH_R(MY_FUNC, __VA_ARGS__)

当前代码产生以下输出:

(FOR_EACH_HELPER_R (MY_FUNC, B, C) | A)

可以看出,扩展不会发生在第一级之后。

我想我必须EXPAND然而,在其他地方,我不知道在哪里。

我想要做的事情完全有可能是不可能的,但是,C++20 之前的递归宏扩展方法(利用PP_NARG)确实适用于嵌套,所以我希望新的、更干净的方法也适用于它!


你的基本FOR_EACH_R是正确的,导致问题的原因是调用func在你的FOR_EACH_HELPER_R macro.

您可以通过暂时删除它来验证这一点:

/* ... */
#define FOR_EACH_HELPER_R(func, sub, ...) (__VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, __VA_ARGS__)), sub)
/* ... */

NEST_RECURSIVE(A, B, C)

神箭示例

会导致:

(((, C), B), A)

1. 宏观评估如何运作

宏观评估不是很直观,所以我将快速解释对此答案很重要的两个概念:

1.1 类函数宏求值顺序

Example:

#define FOO() 1
#define BAR() 2
#define BAZ(A, B) MIAU(A, B)
#define MIAU(A, B) B A

BAZ(FOO(), BAR())

当预处理器看到调用时BAZ()将会发生以下事情:

    1. 参数经过充分评估:
    • FOO() -> 1
    • BAR() -> 2
    1. 计算值被代入宏体中:
    • BAZ(1, 2) -> MIAU(1, 2)
    1. 宏观体全面评估again
    • MIAU(1, 2) -> 2 1

因此,参数可能会被评估两次 - 一次是在它们被替换到宏主体之前,然后是在评估主体时再次。

这也是为什么您可以将宏作为参数传递给其他宏,例如:

#define FOO(fn) fn()
#define BAR() 12

FOO(BAR)

这里会发生以下事情:

    1. 预处理器完全评估参数:
      BAR确实命名了宏,但它不是宏调用。因此预处理器不会评估它并将其视为文本:BAR
    1. 代入宏体:
      FOO(BAR) -> BAR()
    1. 身材评价:
      BAR() -> 12

Your EXPAND宏还使用它来重复强制表达式的求值。

1.2 不能从自身调用宏

e.g.:

#define FOO 1 + FOO

FOO

#define BAR 1 + BAZ
#define BAZ BAR

BAR
  • FOO会导致1 + FOO
  • BAR会导致1 + BAR

本质上,当预处理器正在评估给定的宏时,例如BAR,任何发生BAR在其中(或在它调用的宏之一中)将被标记为不要试图评估这一点 - 永远不要.

所以基本上一旦宏看到它自己的名字,游戏就结束了。


2. 为什么你的例子不起作用

让我们来看看您的评价FOR_EACH_R宏: (我将省略EXPAND为简单起见,使用宏)

  1. First EXPAND round
  • 首先我们从FOR_EACH_HELPER_R(MY_FUNC, A, B, C)
  • 替换到正文中:MY_FUNC(FOR_EACH_AGAIN_R PARENS (MY_FUNC, B, C), A)
  • then evaluate the body
    • the preprocessor sees the call to MY_FUNC, so both arguments will be evaluated:
      • FOR_EACH_AGAIN_R PARENS (MY_FUNC, B, C)变成FOR_EACH_AGAIN_R () (MY_FUNC, B, C)(由于FOR_EACH_AGAIN_R不是直接调用)
      • A -> A
  • 代入MY_FUNC body:
    MY_FUNC((FOR_EACH_AGAIN_R () (MY_FUNC, B, C)), A)
    ->
    (FOR_EACH_AGAIN_R () (MY_FUNC, B, C) | A)
  • 评价身体:(FOR_EACH_AGAIN_R () (MY_FUNC, B, C) | A)
    ->
    (FOR_EACH_HELPER_R (MY_FUNC, B, C) | A)
    ->
    预处理器检测递归(我们在MY_FUNC被称为FOR_EACH_HELPER_R我们正在尝试打电话FOR_EACH_HELPER_R又在这里)
    so the FOR_EACH_HELPER_R将被标记为不要尝试评估这个
    另外MY_FUNC参数也将被标记(因为我们在MY_FUNC)
  1. Every EXPAND在那之后
  • 预处理器尝试计算当前表达式:
    (FOR_EACH_HELPER_R (MY_FUNC, B, C) | A) but FOR_EACH_HELPER_R被标记为不要尝试评估这个,因此该调用被忽略并且没有任何内容被替换。

-> 你最终会得到(FOR_EACH_HELPER_R (MY_FUNC, B, C) | A)作为输出


3. 如何修复

最大的问题是你通过了FOR_EACH_AGAIN_R(...)作为你的论点func,它将对该部分求值两次(一次作为参数,一次在主体中),因此预处理器会看到递归调用并停止。

您可以通过延迟来部分修复它FOR_EACH_AGAIN_R通过另一个评估周期,例如:

/* ... */
#define FOR_EACH_HELPER_R(func, sub, ...) func (__VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, __VA_ARGS__)), sub)
#define FOR_EACH_AGAIN_R() FOR_EACH_AGAIN_R_IMPL PARENS
#define FOR_EACH_AGAIN_R_IMPL() FOR_EACH_HELPER_R
/* ... */

神箭示例

这将导致:

(MY_FUNC (MY_FUNC (, C), B) | A)

现在循环已完全扩展,但是仍然存在递归问题MY_FUNC.
这里的根本问题是参数之一MY_FUNC将包含MY_FUNC, e.g.:

MY_FUNC((FOR_EACH_AGAIN_R PARENS (MY_FUNC, B, C)), A)

所以一旦预处理器替换MY_FUNC into MY_FUNC该令牌将立即标记为永远不要再尝试评估它.
So the MY_FUNC第一次调用后链卡住了。

如果您不需要递归调用,那么会容易得多,例如:

/* ... */
#define FOR_EACH_HELPER_R(func, sub, ...) __VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, __VA_ARGS__)), func(sub)
/* ... */
#define MY_FUNC(var) (var)
/* ... */

神箭示例

会毫无问题地工作(结果是, (C), (B), (A))

如果您绝对需要递归调用,那么只有一种方法:

你需要确保MY_FUNC永远看不到FOR_EACH_HELPER_R & MY_FUNC.
但考虑到每次调用MY_FUNC需要先前调用的结果,您唯一的选择是以所有MY_FUNC呼叫立即进行评估。

例如你需要建立FOR_EACH_HELPER_R以这样的方式,最终你会留下:

MY_FUNC(MY_FUNC(MY_FUNC(, C), B), A)

以便正确评估递归调用。

确保这一点的最简单方法是使用与您使用的相同的延迟技巧FOR_EACH_AGAIN_R,例如有一组像这样的宏:

#define DELAY6(...) DELAY6_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY5(...) DELAY5_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY4(...) DELAY4_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY3(...) DELAY3_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY2(...) DELAY2_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY1(...) DELAY1_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY0(...) __VA_ARGS__

#define DELAY6_IMPL() DELAY5
#define DELAY5_IMPL() DELAY4
#define DELAY4_IMPL() DELAY3
#define DELAY3_IMPL() DELAY2
#define DELAY2_IMPL() DELAY1
#define DELAY1_IMPL() DELAY0

So DELAY6将延迟6次评估,DELAY55个等...

然后你可以用它来延迟调用MY_FUNC, e.g.:

#define FOR_EACH_R(func, ...) __VA_OPT__(EXPAND(FOR_EACH_HELPER_R(func, DELAY6, __VA_ARGS__)))
#define FOR_EACH_HELPER_R(func, del, sub, ...) del(func) (__VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, del(), __VA_ARGS__)), sub)

注意我们正在经过del(), not del到下一个迭代FOR_EACH_HELPER_R,这将有效地导致下一个较低的DELAY*正在传递的函数(以便所有延迟都在一次评估中解决)

With NEST_RECURSIVE(A, B, C, D, E, F, G)这将评估如下:

DELAY5_IMPL () (MY_FUNC) (DELAY5_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY5_IMPL () , C, D, E, F, G), B), A)
->
DELAY4_IMPL () (MY_FUNC) (DELAY4_IMPL () (MY_FUNC) (DELAY4_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY4_IMPL () , D, E, F, G), C), B), A)
->
DELAY3_IMPL () (MY_FUNC) (DELAY3_IMPL () (MY_FUNC) (DELAY3_IMPL () (MY_FUNC) (DELAY3_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY3_IMPL () , E, F, G), D), C), B), A)
->
DELAY2_IMPL () (MY_FUNC) (DELAY2_IMPL () (MY_FUNC) (DELAY2_IMPL () (MY_FUNC) (DELAY2_IMPL () (MY_FUNC) (DELAY2_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY2_IMPL () , F, G), E), D), C), B), A)
->
DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY1_IMPL () , G), F), E), D), C), B), A)
->
((((((( | G) | F) | E) | D) | C) | B) | A)

神箭示例

请注意MY_FUNC直到最后一轮评估才被调用 - 这本质上确保了所有 MY_FUNC 调用都会立即评估,并且我们不会遇到递归宏调用的任何问题。

你必须定义很多DELAY_不过要使其起作用(每个附加参数还有 1 个延迟宏)FOR_EACH_R)

完整代码示例:godbolt

#define PARENS ()

#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__

#define DELAY6(...) DELAY6_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY5(...) DELAY5_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY4(...) DELAY4_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY3(...) DELAY3_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY2(...) DELAY2_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY1(...) DELAY1_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY0(...) __VA_ARGS__



#define DELAY6_IMPL() DELAY5
#define DELAY5_IMPL() DELAY4
#define DELAY4_IMPL() DELAY3
#define DELAY3_IMPL() DELAY2
#define DELAY2_IMPL() DELAY1
#define DELAY1_IMPL() DELAY0


#define FOR_EACH_R(func, ...) __VA_OPT__(EXPAND(FOR_EACH_HELPER_R(func, DELAY6, __VA_ARGS__)))
#define FOR_EACH_HELPER_R(func, del, sub, ...) del(func) (__VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, del(), __VA_ARGS__)), sub)
#define FOR_EACH_AGAIN_R() FOR_EACH_HELPER_R

#define MY_FUNC(nested, var) (nested | var)
#define NEST_RECURSIVE(...) FOR_EACH_R(MY_FUNC, __VA_ARGS__)

NEST_RECURSIVE(A, B, C, D, E, F, G)

4. 可能更好的方法

4.1 简单宏链

上述解决方案需要很好地理解如何评估宏以了解发生的情况。您还可以通过定义一堆宏来选择更简单的方法,例如例如,升压确实

e.g.:


#define FOR_EACH_ERROR()

#define FOR_EACH_R(fn, ...) __VA_OPT__(FOR_EACH_R_IMPL_0(fn, __VA_ARGS__))
#define FOR_EACH_R_IMPL_0(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_1(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_1(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_2(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_2(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_3(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_3(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_4(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_4(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_5(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_5(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_6(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_6(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_7(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_7(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_8(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_8(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_9(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_9(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_10(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_10(...) FOR_EACH_ERROR("Shenanigans!")


#define MY_FUNC(nested, var) (nested | var)
#define NEST_RECURSIVE(...) FOR_EACH_R(MY_FUNC, __VA_ARGS__)

NEST_RECURSIVE(A, B, C, D, E, F, G)

神箭示例

这更容易理解并且也很容易扩展(只需添加更多宏)

4.2 递归左折叠

如果您希望递归版本减少需要编写的行数,可以通过使用左折叠来实现,例如:

// Recursive Left Fold
#define FOR_EACH_L(fn, ...) __VA_OPT__(FOR_EACH_APPLY0(FOR_EACH_RESULT, FOR_EACH_L_4(fn,,__VA_ARGS__)))

#define FOR_EACH_L_4(fn, res, ...) FOR_EACH_APPLY4(FOR_EACH_L_3, FOR_EACH_APPLY4(FOR_EACH_L_3, FOR_EACH_APPLY4(FOR_EACH_L_3, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_3(fn, res, ...) FOR_EACH_APPLY3(FOR_EACH_L_2, FOR_EACH_APPLY3(FOR_EACH_L_2, FOR_EACH_APPLY3(FOR_EACH_L_2, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_2(fn, res, ...) FOR_EACH_APPLY2(FOR_EACH_L_1, FOR_EACH_APPLY2(FOR_EACH_L_1, FOR_EACH_APPLY2(FOR_EACH_L_1, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_1(fn, res, ...) FOR_EACH_APPLY1(FOR_EACH_L_0, FOR_EACH_APPLY1(FOR_EACH_L_0, FOR_EACH_APPLY1(FOR_EACH_L_0, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_0(fn, res, ...) fn, FOR_EACH_FIRST(__VA_OPT__(fn(res, FOR_EACH_FIRST(__VA_ARGS__)), ) res) __VA_OPT__(FOR_EACH_TAIL(__VA_ARGS__))

#define FOR_EACH_APPLY4(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY3(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY2(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY1(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY0(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_FIRST(el, ...) el
#define FOR_EACH_TAIL(el, ...) __VA_OPT__(, __VA_ARGS__)
#define FOR_EACH_RESULT(fn, res, ...) res

神箭示例

左折叠比右折叠更容易实现,因为获取第一个元素__VA_ARGS__比获得最后一个容易得多(FOR_EACH_FIRST在上面的例子中)。

上面提供的版本最多可以处理 81 个参数,如果您需要更多,只需创建更多版本FOR_EACH_L_* & FOR_EACH_APPLY*宏。 (这些宏中的每个附加宏都会使其可以处理的最大参数数量增加三倍)

不过,您确实需要以相反的顺序提供参数,因为它是左折叠,例如:

NEST_RECURSIVE(A, B, C, D, E, F, G)
// would result in ((((((( | A) | B) | C) | D) | E) | F) | G)

如果您需要右折叠,您可以通过反转参数然后调用我们上面创建的左折叠变体来实现它。

e.g.:

// Reverse args
#define PARENS ()

#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__

#define REVERSE(...) __VA_OPT__(EXPAND(REVERSE_HELPER(__VA_ARGS__)))
#define REVERSE_HELPER(el, ...) __VA_OPT__(REVERSE_AGAIN PARENS (__VA_ARGS__), ) el
#define REVERSE_AGAIN() REVERSE_HELPER

神箭示例

(这也仅适用于最多 81 个参数,您可以添加更多EXPAND*宏来增加它可以处理的参数数量)

example:

REVERSE(A, B, C, D, E, F, G)
// would result in G, F, E, D, C, B, A

然后你可以像这样实现正确的折叠:

// Right fold
#define FOR_EACH_R(fn, ...) __VA_OPT__(FOR_EACH_R_APPLY(FOR_EACH_L, fn, REVERSE(__VA_ARGS__)))
#define FOR_EACH_R_APPLY(fn, ...) fn(__VA_ARGS__)

神箭示例

最终给出预期结果(最多 81 个参数),例如:

NEST_RECURSIVE(A, B, C, D, E, F, G)
// would result in: ((((((( | G) | F) | E) | D) | C) | B) | A)

完整代码:godbolt

// Reverse args
#define PARENS ()

#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__

#define REVERSE(...) __VA_OPT__(EXPAND(REVERSE_HELPER(__VA_ARGS__)))
#define REVERSE_HELPER(el, ...) __VA_OPT__(REVERSE_AGAIN PARENS (__VA_ARGS__), ) el
#define REVERSE_AGAIN() REVERSE_HELPER

// Recursive Left Fold
#define FOR_EACH_L(fn, ...) __VA_OPT__(FOR_EACH_APPLY0(FOR_EACH_RESULT, FOR_EACH_L_4(fn,,__VA_ARGS__)))

#define FOR_EACH_L_4(fn, res, ...) FOR_EACH_APPLY4(FOR_EACH_L_3, FOR_EACH_APPLY4(FOR_EACH_L_3, FOR_EACH_APPLY4(FOR_EACH_L_3, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_3(fn, res, ...) FOR_EACH_APPLY3(FOR_EACH_L_2, FOR_EACH_APPLY3(FOR_EACH_L_2, FOR_EACH_APPLY3(FOR_EACH_L_2, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_2(fn, res, ...) FOR_EACH_APPLY2(FOR_EACH_L_1, FOR_EACH_APPLY2(FOR_EACH_L_1, FOR_EACH_APPLY2(FOR_EACH_L_1, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_1(fn, res, ...) FOR_EACH_APPLY1(FOR_EACH_L_0, FOR_EACH_APPLY1(FOR_EACH_L_0, FOR_EACH_APPLY1(FOR_EACH_L_0, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_0(fn, res, ...) fn, FOR_EACH_FIRST(__VA_OPT__(fn(res, FOR_EACH_FIRST(__VA_ARGS__)), ) res) __VA_OPT__(FOR_EACH_TAIL(__VA_ARGS__))

#define FOR_EACH_APPLY4(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY3(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY2(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY1(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY0(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_FIRST(el, ...) el
#define FOR_EACH_TAIL(el, ...) __VA_OPT__(, __VA_ARGS__)
#define FOR_EACH_RESULT(fn, res, ...) res

// Right fold
#define FOR_EACH_R(fn, ...) __VA_OPT__(FOR_EACH_R_APPLY(FOR_EACH_L, fn, REVERSE(__VA_ARGS__)))
#define FOR_EACH_R_APPLY(fn, ...) fn(__VA_ARGS__)

// For testing
#define MY_FUNC(nested, var) (nested | var)
#define NEST_RECURSIVE(...) FOR_EACH_R(MY_FUNC, __VA_ARGS__)


NEST_RECURSIVE(A, B, C, D, E, F, G)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在嵌套上下文中通过 __VA_OPT__ 扩展递归宏 的相关文章

随机推荐

  • 为什么同一层上的文本会重叠 - 即使它具有不透明的背景?

    我知道我可以通过使用相对 绝对定位创建新的堆叠上下文来将元素堆叠在单独的层中 Demo 或不透明度 Demo 然而 我的印象是 默认情况下 html 中更靠下的元素将绘制在前面的元素之上 显然 元素的背景是如此 但我只是注意到文本的工作方式
  • 执行脚本IntrinsicYuvToRgb

    我想将Yuv中的byte 转换为Rgb中的byte ScriptIntrinsic ScriptIntrinsicYuvToRgb 应该执行此操作 基于此example 这是我现在拥有的一些代码 byte imageData gathery
  • 仅当选择 ListViewItem 时显示内容

    我有一个ListBox当其中之一ListBoxItems被选中我想更改按钮 查看 的可见性并显示它 这意味着默认状态是隐藏 这是否可能 如果可以 我是否使用 XAML 中的触发器或后面的代码来解决此问题 XAML 片段
  • 如何检查文件锁定? [复制]

    这个问题在这里已经有答案了 有没有办法在不使用 try catch 块的情况下检查文件是否被锁定 现在 我知道的唯一方法就是打开文件并捕获任何System IO IOException 当我遇到类似的问题时 我完成了以下代码 public
  • “while”和“for”循环的范围是什么?

    a的范围是什么while and for loop 例如 如果我在循环中声明了一个对象 它的行为是什么以及为什么 在以下示例中 循环的每次迭代都会销毁并重新创建所有变量 除了i 它在循环迭代之间持续存在 并且可用于 for 循环中的条件表达
  • Unity 5执行一段代码后无响应

    我绝对是 Unity 的初学者 我一直在开发一个用户界面 它是一个简单的登录表单 其中我有两个Toggle用于选择性别 即男性或女性 我一直在做的是调用一个方法来检查是否已经选择了男性 当按下另一个开关时 它将删除男性的检查 My Unit
  • 有什么方法可以使 {% extends '...' %} 有条件吗? - 姜戈

    我想在 AJAX 和常规 HTTP 调用之间共享一个模板 唯一的区别是一个模板需要与base htmlhtml 另一个没有 任何想法 其他答案要求您传递额外的上下文变量 但只要可以访问请求对象 就没有必要 extends request i
  • 从左到右显示 ImageView 的动画[重复]

    这个问题在这里已经有答案了 可能的重复 动画 ImageView 宽度而不缩放 我想做的是创建一个动画 其中 ImageView 从左到右显示 剪辑动画 图像不应缩放 我尝试更改scaleType 然后将ScaleAnimation直接应用
  • 堆叠条形图,每个堆叠独立的填充顺序

    我面临着一种行为ggplot2 排序和堆积条形图我无法理解 我读过一些关于它的问题 here here等等 但不幸的是我找不到适合我的解决方案 也许答案很简单 但我看不到 希望这不是一个骗子 我的主要目标是根据排序列 此处称为orderin
  • java数学计算给定半径1.00的圆的坐标

    在我的一项作业中 我被要求编写一个程序来计算半径为 1 0 的圆上的点的 x y 坐标 以 0 1 为增量显示范围从 1 00 到负 1 00 的所有 x 值的 y 值输出 并使用以下命令整齐地显示输出printf 其中所有 x 值都垂直对
  • 如何直接进入函数而不进入参数的中间函数?

    在 Visual Studio 2013 或任何版本 中 我正在调试 C 项目 我希望能够绕过单步执行由于参数传递到感兴趣的函数而被调用的中间函数 我怎样才能绕过进入这些中间函数 不直接感兴趣 并直接进入我感兴趣的函数 例如 假设我有一个函
  • 如何在freemarker中自定义数字格式?

    我正在使用 freemarker 并尝试以这种格式显示数字 3 343 434 00例如 这很容易通过使用来解决 total string currency 假设 总数 是某个数字 但是 当我有负数时 它会像这样显示 343 34 而不是这
  • 比较 R 中的 svd 和 princomp

    我想要得到singular values of a matrix in R获取主成分 然后也使用 princomp x 来比较结果 我知道 princomp 会给出主要成分 Question 如何从 d u 和 v 中获取主成分 解决方案s
  • 使用Python组合所有视频的特征

    假设我有 20 个视频 它们具有相同的场景 尺寸和来自同一台相机 让我们假设这二十个视频中的一个有一个人走过 所有其他视频大多相同 除了微小的自然变化 例如风吹树叶等 我正在寻找一种将所有 20 个视频合并为 1 个视频的好方法 我所说的合
  • 尝试加载使用存储传递的数据

    我正在尝试获取正在传递的数据并将其加载到各种文本字段中 现在 我有一个 html 文件 其中包含以下内容 然后 我有一个 js 文件 其中包含我的整个布局以及所有文本字段等以及我的整个商店 到目前为止 我的商店尝试加载数据 但我不确定尝试从
  • 如何启动 ajax 推送网站(activemq 或 cometd 或其他)?

    我想启动一个使用 ajax 推送的应用程序 但是应该正确配置 Web 服务器 并且我不知道如何在服务器端组件上启动 我想从道场彗星然后读了一些博客说activeMQ是较旧的 也是 ajax 推送方面的旗舰载体 但也有另一个博客说它很难设置并
  • 使用 Provider 和模型类搜索/过滤 ListView

    我想通过在搜索字段中输入的文本来过滤列表视图 在线和本网站上有很多示例 但所有内容都过于简化 所有内容都在一个有状态的小部件中 和 或看起来有点混乱 可能不是构建事物的最佳方式 我有一个简单的应用程序 它使用 Provider 一个模型类
  • 如何在reactjs中显示多个标签?

    我制作了一张添加新用户卡 上面有一个加号 我制作了额外的卡片 在每张卡片上显示用户名 显示用户名的卡片使用以下方式显示 map 方法 现在 如果我将第 1 部分代码 见下文 插入到 map 功能后会显示多个添加新用户卡 我的最终目标是显示单
  • 当您使用内存覆盖前缀但所有操作数都是寄存器时会发生什么?

    当您使用内存覆盖前缀但所有操作数都是寄存器时会发生什么 那么 假设您编写了代码mov eax ebx or add eax ebx默认值为 32 位 但您使用 67h 覆盖 处理器如何处理这种情况 英特尔软件开发人员手册 第 2 卷 第 2
  • 如何在嵌套上下文中通过 __VA_OPT__ 扩展递归宏

    我读过了本文 这说明了如何 VA OPT 函数宏可用于递归扩展宏 我想实现类似的东西 不同之处在于宏是在嵌套上下文中扩展的 输入 NEST RECURSIVE A B C 应该产生 顺序无关 C B A 我的方法稍微概括了这篇文章 defi