BOOST_PP_SEQ_FOLD_LEFT如何工作
How does BOOST_PP_SEQ_FOLD_LEFT work?
我需要编写一个宏来处理任意长的东西列表,如(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 预处理器-fu。
假设符合 std 的预处理器是可以的,因为我不需要 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)
会引起扩张的"连锁反应",如下所示:
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
预处理器中不允许递归,但这种类型的链式反应不是递归,因为宏的调用只发生在前一个宏的扩展之后,而不是在扩展期间,因为(
不是扩展的一部分。(虽然,见 https://wg21.link/cwg268)
不幸的是,虽然这将很好地循环(A)(B)(C)
序列,但它会在末尾留下一个额外的标记:两个使用的宏之一的名称。我用来摆脱那个的技巧是用另一个宏调用包装整个列表,该宏调用将在完全扩展后附加(使用 concat 运算符##
_END
,因此它将变为:
[1] [2] [3] [4] a_END
然后我们可以通过定义来简单地摆脱最后一个令牌:
#define a_END
#define b_END
如果我们不能包装整个列表,就没有办法知道我们什么时候到达了最后一个元素。唯一发生的事情是,最后一个a
或b
被保留下来,没有紧随其后的(
,这意味着它根本不会扩展,因为a
和b
是函数样式的宏。(而且我们不能只定义a
和b
扩展到无,因为a
和b
已经是宏,尽管它们不会在没有(
的情况下扩展。
为什么是两个宏?
当我们尝试引起像上面这样的连锁反应时,只有一个这样的宏:
#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
被标记为"不可扩展",我们的连锁反应在第一步后就在那里停止。我们通过使用两个宏来解决此问题:a(..)
不会生成任何具有自己名称的令牌,而只会生成具有其他宏b
的名称的令牌。a
的展开就在那里结束,在b
被展开之前,因为在b之后还没有(
,因为我们在a的展开中"内部"。扩展完成后,我们不再"在"a
内,重新检查令牌,并找到b
的正确调用:b(..)
。那个将再次产生一个名为a
的代币,但由于我们不再处于第一个a
的扩展中,这个不会被标记为"不可展开",连锁反应仍在继续。
好吧,这是我快速而肮脏的东西,我认为你可以使用:
#include <iostream>
#define LIST (1)(2)(3)(4)
#define EAT2(list)
#define EAT(list) EAT2 list
#define KEEP(x) x EAT2(
#define STRINGIFY2(x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define HEAD(list) KEEP list )
#define TAIL(list) EAT(list)
int main()
{
std::cout << STRINGIFY(HEAD(LIST)) << std::endl;
std::cout << STRINGIFY(TAIL(LIST)) << std::endl;
}
基本上,您需要对如何调用宏变得棘手。
举个例子:
HEAD((1)(2))
扩展到
KEEP (1)(2) )
扩展到
1 EAT2 ((2))
扩展到
1
这不是一个完整的答案,但我认为可以作为你想做的事情的起点。
编辑
我现在已经弄清楚了如何提升。PP进行迭代,它并不漂亮,你基本上手动写出迭代,直到一些最大大小。
#define CONCAT2(x, y) x##y
#define CONCAT(x, y) CONCAT2(x, y)
#define SEQ_SIZE(seq) CONCAT(SEQ_SIZE_, SEQ_SIZE_0 seq)
# define SEQ_SIZE_0(_) SEQ_SIZE_1
# define SEQ_SIZE_1(_) SEQ_SIZE_2
# define SEQ_SIZE_2(_) SEQ_SIZE_3
# define SEQ_SIZE_3(_) SEQ_SIZE_4
# define SEQ_SIZE_4(_) SEQ_SIZE_5
# define SEQ_SIZE_5(_) SEQ_SIZE_6
# define SEQ_SIZE_6(_) SEQ_SIZE_7
# define SEQ_SIZE_7(_) SEQ_SIZE_8
# define SEQ_SIZE_8(_) SEQ_SIZE_9
# define SEQ_SIZE_9(_) SEQ_SIZE_10
# define SEQ_SIZE_10(_) SEQ_SIZE_11
# define SEQ_SIZE_SEQ_SIZE_0 0
# define SEQ_SIZE_SEQ_SIZE_1 1
# define SEQ_SIZE_SEQ_SIZE_2 2
# define SEQ_SIZE_SEQ_SIZE_3 3
# define SEQ_SIZE_SEQ_SIZE_4 4
# define SEQ_SIZE_SEQ_SIZE_5 5
# define SEQ_SIZE_SEQ_SIZE_6 6
# define SEQ_SIZE_SEQ_SIZE_7 7
# define SEQ_SIZE_SEQ_SIZE_8 8
# define SEQ_SIZE_SEQ_SIZE_9 9
# define SEQ_SIZE_SEQ_SIZE_10 10
#define MAKE_VAR(elem)
float CONCAT(var_, elem) = 0;
#define MAKE_LIST_0(op, list)
#define MAKE_LIST_1(op, list) op (HEAD(list)) MAKE_LIST_0(op, TAIL(list))
#define MAKE_LIST_2(op, list) op (HEAD(list)) MAKE_LIST_1(op, TAIL(list))
#define MAKE_LIST_3(op, list) op (HEAD(list)) MAKE_LIST_2(op, TAIL(list))
#define MAKE_LIST_4(op, list) op (HEAD(list)) MAKE_LIST_3(op, TAIL(list))
#define MAKE_LIST_5(op, list) op (HEAD(list)) MAKE_LIST_4(op, TAIL(list))
#define MAKE_LIST_6(op, list) op (HEAD(list)) MAKE_LIST_5(op, TAIL(list))
#define MAKE_LIST_7(op, list) op (HEAD(list)) MAKE_LIST_6(op, TAIL(list))
#define MAKE_LIST_8(op, list) op (HEAD(list)) MAKE_LIST_7(op, TAIL(list))
#define MAKE_LIST_9(op, list) op (HEAD(list)) MAKE_LIST_8(op, TAIL(list))
#define MAKE_LIST_10(op, list) op (HEAD(list)) MAKE_LIST_9(op, TAIL(list))
#define MAKE_LIST(op, list) CONCAT(MAKE_LIST_, SEQ_SIZE(list)) (op, list)
int main()
{
MAKE_LIST(MAKE_VAR, LIST)
}
在此上运行预处理器将产生以下结果:
int main()
{
float var_1 = 0; float var_2 = 0; float var_3 = 0; float var_4 = 0; float var_5 = 0;
}
如愿以偿。我相信这可以简化一点,但我希望这有所帮助。
这是我的小2美分:
我记得 Boost.Preprocessor 中使用的预处理器元编程技术的问题是,在折叠序列时不可能拥有任意长的元素列表。
您需要拥有与最大迭代一样多的宏,因此它可以是任意
的,但可以达到最大值。但是,我想知道您是否甚至可以扩展逗号,因为通常这是基于与停止条件宏或下一个迭代宏旁边的内容连接。而且我不明白如何从级联宏扩展逗号,因为串联将不再起作用。
在这种情况下,如果您可以更改一点 API,我会做的是:
#define EXPAND(...) __VA_ARGS__
#define template_(X, Y)
template<EXPAND X
, int dummy = 0
Y
>
#define requires(...)
COMMA() std::enable_if_t< dummy == 0 && (__VA_ARGS__) > = 0
#define COMMA() ,
所以用一个狡猾的更改 API:
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>) > = 0 ,
std::enable_if_t< dummy == 0 && (IsBaz<C>) > = 0 >
void frobozzle(A, B, C);
不完全是请求的API,但优点是你可以用逗号要求表达式,这要归功于使用VA_ARGS的要求宏:
template_((class A, class B, class C),
requires(IsBaseOf<B,C>)
)
int boo()
我努力制作一个无限的FOLD_LEFT,它看起来不在我的:D范围内。
我没有尝试的是重现与您提供的表达式模板相同的输入语法,这对我来说听起来终于更可行了。
- QSqlquery prepare()和bindvalue()不工作
- 导入库可以跨dll版本工作吗
- 以螺旋方式打印矩阵的程序.(工作不好)
- 对象指针在c++中是如何工作的
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++为线程工作动态地分割例程
- 为什么我的 std::ref 无法按预期工作?
- 布尔比较运算符是如何在C++中工作的
- SampleConsensusPrerejective(ext.RANSAC)是如何真正工作的
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- <<操作员在下面的行中工作
- 有人能解释一下为什么下界是这样工作的吗C++的
- ExtractIconEx:可以工作,但偶尔会崩溃
- C++中的memset函数工作不正常
- 当我在第一个循环中使用"auto"时,它工作正常,但是使用"int"它会给出错误,为什么?
- 链表c++插入,所有情况都已检查,但没有任何工作
- 当 int 方法工作正常时,void 方法有何不同,或者为什么我不能调用 void 方法?