我需要编写一个宏来处理任意长的列表,例如(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支持。
答案 0 :(得分:12)
如果在语法中添加一组额外的括号,则可以在不限制“必需”子句的数量的情况下使用相对较少的宏:
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
已经是宏,但是如果没有{(
它们将不会展开1}}。)
当我们试图引起像上面这样的连锁反应时,只有一个像这样的宏:
#define a(x) [x] a
它不起作用:
a (1) (2) (3) (4)
[1] a (2) (3) (4) // Doesn't expand further
这是因为'(无限)递归保护'的工作原理:如果在扩展宏期间,会生成一个扩展宏名称的令牌,它被标记为'unexpandable',这意味着它永远不会再扩张。见http://eel.is/c++draft/cpp.rescan#2
这意味着展开的a
被标记为“不可扩展”,我们的连锁反应在第一步之后停在那里。我们通过使用两个宏来解决此规则来避免这种情况:a(..)
不会生成任何具有自己名称的令牌,而只会生成另一个宏b
的名称。在a
展开之前,b
的扩展就在那里结束,因为在b之后还没有(
,因为我们在'内部'扩展了一个。扩展完成后,我们不再“在'a
内,重新检查令牌,并找到b
的正确调用:b(..)
。那个会再次生成一个名为a
的令牌,但由于我们不再处于第一个a
的扩展中,因此这个不会被标记为'不可扩展',而链反应仍在继续。
答案 1 :(得分:2)
好吧,这是一个快速而又肮脏的东西我掀起来,我认为你可以使用:
#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
这不是一个完整的答案,但我认为可以作为你想要做的事情的起点。
编辑
我现在已经弄清楚BOOST.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 :(得分: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。
我没有尝试再现与您提供的表达式模板相同的输入语法,这听起来对我来说更加可行。