c++ - BOOST_PP_SEQ_FOLD_LEFT 是如何工作的?

标签 c++ c c-preprocessor

我需要编写一个宏来处理任意长的列表,例如 (A)(B)(C)。如果我可以采用 Boost 依赖项,我将只使用 BOOST_PP_SEQ_ 之一宏系列。不幸的是,我不能,所以我只能尝试弄清楚它是如何工作的。这东西并不明显。

这里有人能写一个简单的、独立的实现吗,比方说,BOOST_PP_SEQ_FOLD_LEFT给我看看?特别是,我想转换:

template_(class A, class B, class C)(
    requires IsFoo<A> && IsBar<B>)(
    requires IsBaz<C>)
void frobozzle(A, B, C);

重写为:

template<class A, class B, class C,
    int dummy = 0,
    std::enable_if_t<dummy == 0 && (IsFoo<A> && IsBar<B>), int> = 0,
    std::enable_if_t<dummy == 0 && (IsBaz<C>), int> = 0>
void frobozzle(A, B, C);

可以有任意数量的 requires 子句,并且每个子句都应该有自己的 enable_if_t。我让它与一个 requires 子句一起工作,但我在这个过程中耗尽了我的 C 预处理器功能。

可以假设一个符合标准的预处理器,因为我不需要 MSVC 支持。

最佳答案

如果您在语法中添加一组额外的括号,则可以不受“必需”子句数量的限制,并且使用相对较少的宏:

template_((class A, class B, class C)
    (requires IsFoo<A> && IsBar<B>)
    (requires IsBaz<C>)
)
void frobozzle(A, B, C);

宏:

#define template_(...) template_impl_ADD_END(template_impl_LIST __VA_ARGS__) >

#define template_impl_ADD_END(...) template_impl_ADD_END2(__VA_ARGS__)
#define template_impl_ADD_END2(...) __VA_ARGS__ ## _END

#define template_impl_LIST(...) template<__VA_ARGS__, int dummy = 0 template_impl_LIST_1

#define template_impl_LIST_1(...) , std::enable_if_t<dummy == 0 && template_impl_REQUIRES(__VA_ARGS__), int> = 0 template_impl_LIST_2
#define template_impl_LIST_2(...) , std::enable_if_t<dummy == 0 && template_impl_REQUIRES(__VA_ARGS__), int> = 0 template_impl_LIST_1

#define template_impl_REQUIRES(...) (template_impl_REQUIRES_ ## __VA_ARGS__)
#define template_impl_REQUIRES_requires

#define template_impl_LIST_END
#define template_impl_LIST_1_END
#define template_impl_LIST_2_END

使用这些宏,上面的示例扩展为:

template <class A, class B, class C,
          int dummy = 0,
          std::enable_if_t<dummy == 0 && (IsFoo<A> && IsBar<B>), int> = 0,
          std::enable_if_t<dummy == 0 && (IsBaz<C>), int> = 0>

void frobozzle(A, B, C);

说明

考虑这些宏:

#define a(x) [x] b
#define b(x) [x] a

有了这些,这个:

a (1) (2) (3) (4)

会引起膨胀的“链式 react ”如下:

a (1) (2) (3) (4)
[1] b (2) (3) (4)
[1] [2] a (3) (4)
[1] [2] [3] b (4)
[1] [2] [3] [4] a

预处理器中不允许递归,但这种类型的链式 react 不是递归,因为宏的调用只发生在前一个扩展之后,而不是在扩展期间,因为 (不是扩展的一部分。(虽然,请参阅 https://wg21.link/cwg268 )

不幸的是,虽然这会很好地遍历一系列 (A)(B)(C),但它会在末尾留下一个额外的标记:所使用的两个标记之一的名称宏。我用来摆脱那个的技巧是用另一个宏调用包装整个列表,它将在完全展开,所以它将变成:

[1] [2] [3] [4] a_END

然后我们可以简单地通过定义去掉最后一个标记:

#define a_END
#define b_END

如果我们不能包装整个列表,就无法知道何时到达最后一个元素。唯一发生的事情是最后一个 ab 没有跟在它后面的 ( ,这意味着它根本不会扩展,因为 ab 是函数样式的宏。(而且我们不能只定义 ab 展开为空,因为 ab 已经是宏,尽管它们在没有 (.)

的情况下不会展开

为什么是两个宏?

当我们试图用像上面这样的一个宏引起链式 react 时:

#define a(x) [x] a

它不会工作:

a (1) (2) (3) (4)
[1] a (2) (3) (4) // Doesn't expand further

这是因为“(无限)递归保护”是如何工作的:如果在宏扩展期间,产生了一个带有被扩展宏名称的标记,它被标记为“不可扩展”,这意味着它永远不会再扩大。参见 http://eel.is/c++draft/cpp.rescan#2

这意味着扩展的 a 被标记为“不可扩展”,我们的链式 react 在第一步之后就停止了。我们通过使用两个宏来解决此规则来避免这种情况:a(..) 不会生成任何具有自己名称的标记,而只会生成另一个宏 ba 的扩展就在那里结束,在 b 被扩展之前,因为在 b 之后还没有 (,因为我们在“内部” a 的扩展。扩展完成后,我们不再“内部”a,重新检查标记,正确调用 b 是找到:b(..)。那个将再次生成一个名为 a 的标记,但是因为我们不再处于第一个 的扩展中>a,这个不会被标记为'unexpandable',链式 react 继续。

关于c++ - BOOST_PP_SEQ_FOLD_LEFT 是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48307385/

相关文章:

c++ - 打印从数字创建的可能字符串

c - #define 宏用于 C 中的调试打印?

c++ - 成员函数的宏错误

c++ - 在 sizeof(++n) 表达式中不调用增量运算符

c++ - const HANDLE 真的是 const 吗?

c++ - 在 C++11 中使用右值引用

c - c中递归函数的输出

c - 保持 Ruby C 扩展代码干燥?

c++ - 我可以在处理完内置的 wxWidgets 事件后*运行代码吗?

c++ - STL iota 包含文件更改