在MACRO(X)(Y)中交换X和Y的顺序

6

我有一个基于C++宏的DSL,定义了如下所示的一个宏:

#define RETURNS(...) \
    enable_if_t<__VA_ARGS__ WHEN

#define WHEN(...) \
    , EAT_ ## __VA_ARGS__ >

#define EAT_requires

这是用于类似以下方式的:

template<class T>
auto some_function(T t) ->
    RETURNS(int)
        (requires SomeConcept<T>)

这将扩展为:

template<class T>
auto some_function(T t) ->
    enable_if_t<int, SomeConcept<T>>

当启用C++20概念时,它会扩展为一个真正的requires子句。

我更喜欢参数的顺序被翻转。也就是说,我希望它生成这个:

template<class T>
auto some_function(T t) ->
    enable_if_t<SomeConcept<T>, int>

我觉得这是不可能的。有没有聪明的PP黑客可以证明我错了?

2
为什么要使用宏?几乎总有更好的方法。 - Jesper Juhl
2
定义一个新的 template<A,B> enable_if_t_reversed<A,B> : enable_if_t<B,A> 是否可行? - Nican
4
"为什么使用宏?"-- 看起来你忽略了这是一个可移植性宏,当支持C++20的概念时会扩展为其他内容。“只定义一个新的'template<A,B> enable_if_t_reversed<A,B> : enable_if_t<B,A>'会行吗?”-- 这是我目前正在做的,而且并不糟糕,但即使当'A'为假时,它也强制评估类型'B'。如果我能找到一种重新排序参数的方法,我可以改进编译时间。 - Eric Niebler
@EricNiebler 但是,在 enable_if_t 内部没有短路 -- 反转参数不会改变任何东西。 - Quentin
1
@Quentin 是的,它不是基于参数顺序的。问题在于 fast_enable_if<false>::sfinae 已经是一种替换失败了 - 而我们是从左到右实例化的。将条件放在第一位可以让你摆脱这个问题。(此外,在这种情况下,我们并不是试图避免由于硬错误而实例化,而是试图避免由于额外的编译时间工作而实例化) - Barry
显示剩余3条评论
4个回答

4
如果可以忍受省略开括号,你可以这样实现:
#define UNWRAP(...) __VA_ARGS__

#define RETURNS(...) \
    WHEN ((__VA_ARGS__),

#define WHEN(x, ...) \
    enable_if_t<EAT_ ## __VA_ARGS__, UNWRAP x>

#define EAT_requires

template<class T>
auto some_function(T t) ->
    RETURNS(pair<int, int>)
        requires SomeConcept<T, int>)

输入:

template<class T>
auto some_function(T t) ->
    RETURNS(pair<int, int>)
        requires SomeConcept<T, int>)

输出:

template<class T>
auto some_function(T t) ->
    enable_if_t< SomeConcept<T, int>, pair<int, int> >

不错的hack,但是IDE不会高兴。 - Nikki Chumakov

3
为什么不直接使用类似于的东西呢?
template<class T,bool B>
using reverse_enable_if_t=enable_if_t<B,T>;

1
另一种看法是:

#define RETURNS(...) REVERSE_ENABLE_IF_T((__VA_ARGS__))
#define REQUIRES ),(
#define UNWRAP(...) __VA_ARGS__        
#define REVERSE_ENABLE_IF_T(PT,PB) enable_if_t<UNWRAP PB,UNWRAP PT>

这允许使用括号平衡的语法:

template<class T>
auto some_function(T t) ->
  RETURNS(pair<int, int> REQUIRES SomeConcept<T, int>);

0

这里有一个解决方案,它不需要不匹配的括号,但需要在整个表达式周围添加一个EVAL宏:

#define EVAL( ... )  EVAL1( __VA_ARGS__ )
#define EVAL1( ... ) EVAL0( EVAL0( EVAL0( __VA_ARGS__ ) ) )
#define EVAL0( ... ) __VA_ARGS__

#define EMPTY()
#define DEFER( ... ) __VA_ARGS__ EMPTY()

#define EAT_requires
#define SWAP( X, Y )   enable_if_t<EAT_##Y, X>
#define RETURNS( X )   DEFER( SWAP )( X, ADD_PAREN
#define ADD_PAREN( Y ) Y )

你可以接着写下面的代码:
EVAL(
  template<class T>
  auto some_function(T t) ->
    RETURNS(int)
      (requires SomeConcept<T>)
)

它会生成:

template<class T>
auto some_function( T t ) ->
  enable_if_t<SomeConcept<T>, int>

谢谢。我正在尝试避免将声明包装在“EVAL”宏中。 - Eric Niebler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接