【问题标题】:How to expand a recursive macro via __VA_OPT__ in a nested context如何在嵌套上下文中通过 __VA_OPT__ 扩展递归宏
【发布时间】:2022-01-11 06:35:15
【问题描述】:

我读过this article,它说明了__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)确实适用于嵌套,所以我希望新的,更清洁,方法也适用!

【问题讨论】:

  • 当有这么多(元)模板选项可用时,为什么要把所有精力都放在 MACRO(特别是使用 C++20)上?所以我的问题是你想解决什么,可能有比宏更好的方法。阅读:stackoverflow.com/questions/14041453/….
  • 我不明白这个问题。你想要的输出是什么,宏“签名”应该是什么样子?
  • @PepijnKramer 当然你是对的,但在这种情况下我没有太多选择,因为我使用它来扩展一个本身依赖于宏的测试框架,所以没有别的可悲的是,这是一种选择。
  • @ViralTaco_ 输入:NEST_RECURSIVE(A, B, C),输出:((( | C) | B) | A),签名:NEST_RECURSIVE(...)
  • @Salvage Meh,这是次优的。好吧,我很久以前就失去了所有的宏观技能,所以帮不了你。希望别人可以

标签: c++ macros c-preprocessor c++20 variadic-macros


【解决方案1】:

您的基本FOR_EACH_R 是正确的,导致问题的原因是在您的FOR_EACH_HELPER_R 宏中调用func

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

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

NEST_RECURSIVE(A, B, C)

godbolt example

会导致:

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

1。宏观评估的工作原理

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

1.1 类函数宏求值顺序

例子:

#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. 宏体被全面评估再次
    • MIAU(1, 2) -> 2 1

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

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

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

FOO(BAR)

这里会发生以下事情:

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

您的 EXPAND 宏也使用它来重复强制对表达式求值。

1.2 不能从自身调用宏

例如:

#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. 第一轮EXPAND
  • 首先我们从FOR_EACH_HELPER_R(MY_FUNC, A, B, C)开始
  • 替换为正文:MY_FUNC(FOR_EACH_AGAIN_R PARENS (MY_FUNC, B, C), A)
  • 然后评估正文
    • 预处理器看到对MY_FUNC的调用,所以两个参数都会被计算:
      • 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正文:
    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
    所以FOR_EACH_HELPER_R 将被标记为不要尝试评估这个
    另外MY_FUNC 参数也将被标记(因为我们在MY_FUNC
  1. 此后每EXPAND 一轮
  • 预处理器尝试计算当前表达式:
    (FOR_EACH_HELPER_R (MY_FUNC, B, C) | A) 但是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
/* ... */

godbolt example

这将导致:

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

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

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

因此,一旦预处理器将MY_FUNC 替换为MY_FUNC,该标记将立即标记为永远不要再尝试评估它
所以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)
/* ... */

godbolt example

可以正常工作(结果为, (C), (B), (A)

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

您需要确保MY_FUNC 永远不会看到FOR_EACH_HELPER_RMY_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

所以DELAY6 会延迟 6 次评估,DELAY5 会延迟 5 次,等等...

然后你可以用它来延迟调用MY_FUNC,例如:

#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() 传递给FOR_EACH_HELPER_R 的下一次迭代,而不是del,这将有效地导致下一个较低的DELAY* 函数被传递(这样所有的延迟都会在一个单一的评价)

使用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)

godbolt example

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

您必须定义很多 DELAY_ 宏才能使其正常工作(FOR_EACH_R 的每个附加参数需要多 1 个延迟宏)

完整代码示例: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 简单的宏链

上述解决方案需要非常了解如何评估宏才能了解正在发生的事情。你也可以通过定义一堆宏来选择更简单的方法,比如boost does for example

例如:


#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)

godbolt example

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

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

godbolt example

左折叠比右折叠更容易实现,因为获取__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)

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

例如:

// 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

godbolt example

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

示例:

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__)

godbolt example

最终为您提供预期结果(最多 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)

【讨论】:

  • 非常感谢您的详细解答!你的解释很有启发性。看到这里不可能实现每个 SLOC 的最大参数计数的指数增长,这在非嵌套“现代”循环中是不可能的,这有点令人遗憾,因为这是我最喜欢的方面。但是,非常感谢您抽出宝贵的时间来写这篇文章!
  • @Salvage 如果您正在寻找递归解决方案,那绝对是可能的。但是你必须把它分成两部分:一个左折叠(这比宏中的右折叠更容易实现)和一个用于反转参数的额外宏。 godbolt example 但与复制粘贴同一行并替换数字相比,它相当复杂(就像我的回答一样)
  • 该示例最多只能在 27 个大气压下工作,但您可以轻松添加更多 FOR_EACH_L_*FOR_EACH_APPLY*EXPAND* 宏来增加其最大参数数。
  • 所以,如果我没记错的话,这也表明每个 SLOC 的参数数量也呈指数增长?这是太棒了!再次感谢您!
  • @Salvage 是的,完全正确 :) Godbolt 中的版本最多只能包含 27 个参数,但您可以为添加的每个额外宏增加三倍的容量。我已经更新了我的答案以包含递归版本,并带有 godbolt 示例:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-09
  • 2016-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多