tl;博士:
-
因为它是 BOOST_AUTO_FUNCTION 在 C++11 中无法实现
- SFINAE 部件可以正常工作
- 由于未计算的上下文中不允许使用 lambda,因此不可能从任意语句中推导返回类型
-
BOOST_AUTO_FUNCTION 的略微修改版本是可能的,它不会从表达式中推断出返回类型,而是从另一个参数中推断出来。
例如:
template< class L, class R >
BOOST_AUTO_FUNCTION( operator -( L const& lhs, R const& rhs ),
lhs + -rhs // <- this will be used to deduce the return type
, if ( is_vector_udt< L > )
( is_vector_udt< R > )
, try ( lhs + rhs )
( -rhs )
, if typename ( L::value_type )
)
(
return lhs + -rhs
)
-
这是修改后的BOOST_AUTO_FUNCTION 的完整实现:
本文的其余部分将详细介绍如何在 C++11 中使用没有任何库的宏构建 BOOST_AUTO_FUNCTION:
1。解析条件
让我们首先从一个宏开始,它可以解析来自BOOST_AUTO_FUNCTION 宏的单个条件列表(例如,if (A)(B)(C) / try (A)(B) 成我们可以在 C++ 中使用的东西。
这样做的基础是 - 正如 @chris 在 cmets 中提到的 - concatenation。通过添加一个固定标记,我们可以将if / try 转换为宏名称以进行进一步处理。
这样的PARSE_COND 宏可能如下所示:
#define CONCAT_IMPL(a, b) a ## b
#define CONCAT(a,b) CONCAT_IMPL(a, b)
#define COND_if <-IF->
#define COND_try <-TRY->
#define PARSE_COND(expr) CONCAT(COND_,expr)
这已经允许我们用我们想要的任何东西替换前面的 if / try。
宏扩展示例:godbolt
PARSE_COND(if (A)(B)) => <-IF-> (A)(B)
PARSE_COND(try (A)(B)) => <-TRY-> (A)(B)
PARSE_COND(if typename (A)(B)) => <-IF-> typename (A)(B)
我们可以使用此技术将if / try 替换为调用另一组宏 (HANDLE_IF / HANDLE_TRY),这些宏将处理下一阶段的处理:godbolt
#define COND_if HANDLE_IF (
#define COND_try HANDLE_TRY (
#define HANDLE_IF(expr) DOIF: expr
#define HANDLE_TRY(expr) DOTRY: expr
#define PARSE_COND(expr) CONCAT(COND_,expr))
PARSE_COND(if (A)(B)) => DOIF: (A)(B)
PARSE_COND(try (A)(B)) => DOTRY: (A)(B)
PARSE_COND(if typename (A)(B)) => DOIF: typename (A)(B)
1.1 处理多令牌标识符,如if typename
现在我们需要处理区分普通的if 和if typename。
不幸的是,我们不能使用与if / try 相同的方法,因为连接需要产生一个有效的预处理器令牌 - 并且将某些内容连接到左括号总是会产生一个无效的令牌 => 我们会得到一个预处理器错误。
例如像这样的东西对于if typename 可以正常工作,但只使用if 会导致错误:godbolt
#define COND_IF_typename HANDLE_IF_TYPENAME (
#define COND_IF_(...) HANDLE_IF_IF ( (__VA_ARGS__)
#define HANDLE_IF(expr) CONCAT(COND_IF_, expr))
#define HANDLE_IF_TYPENAME(expr) DOIFTYPENAME: expr
#define HANDLE_IF_IF(expr) DOIF: expr
PARSE_COND(if typename (A)(B)) => DOIFTYPENAME: (A)(B)
PARSE_COND(if (A)(B)) => <compiler error>
所以我们需要一种方法来检测是否还有更多的标记需要解析(例如typename),或者我们是否已经达到括号中的条件。
为此,我们将不得不使用一些宏恶作剧 - 实际上可以通过使用类似函数的宏来检查表达式是否以括号开头。
如果类函数宏的名称后跟括号,它将展开,否则宏的名称将保持不变。
示例:godbolt
#define CHECK(...) EXPANDED!
#define EXPANSION_CHECK(expr) CHECK expr
EXPANSION_CHECK((A)(B)) => EXPANDED!(B)
EXPANSION_CHECK(typename (A)(B)) => CHECK typename (A)(B)
通过使用类函数宏的这个属性,我们可以编写一个可以检测给定表达式中是否有更多标记的宏:godbolt
#define EXPAND(...) __VA_ARGS__
#define EMPTY()
#define DEFER(id) id EMPTY()
#define HAS_MORE_TOKENS(expr) EXPAND(DEFER(HAS_MORE_TOKENS_RESULT)(HAS_MORE_TOKENS_CHECK expr, 0, 1))
#define HAS_MORE_TOKENS_CHECK(...) ~,~
#define HAS_MORE_TOKENS_RESULT(a, b, c, ...) c
HAS_MORE_TOKENS(typename (A)(B)) => 1
HAS_MORE_TOKENS((A)(B)) => 0
使用它,我们现在可以处理任何标记序列 - 如果有更多标记,我们可以使用CONCAT()-trick 来进一步扩展它们,如果我们已经达到括号中的条件,我们可以停止并知道哪个序列我们以前读过的令牌。
示例:godbolt
#define CONCAT_IMPL(a, b) a ## b
#define CONCAT(a,b) CONCAT_IMPL(a, b)
#define EXPAND(...) __VA_ARGS__
#define EMPTY()
#define DEFER(id) id EMPTY()
#define HAS_MORE_TOKENS(expr) EXPAND(DEFER(HAS_MORE_TOKENS_RESULT)(HAS_MORE_TOKENS_CHECK expr, 0, 1))
#define HAS_MORE_TOKENS_CHECK(...) ~,~
#define HAS_MORE_TOKENS_RESULT(a, b, c, ...) c
#define IIF(condition, a, b) CONCAT(IIF_, condition)(a, b)
#define IIF_0(a, b) b
#define IIF_1(a, b) a
#define COND_if HANDLE_IF (
#define COND_try HANDLE_TRY (
#define COND_IF_typename HANDLE_IF_TYPENAME (
#define HANDLE_IF(expr) IIF(HAS_MORE_TOKENS(expr), HANDLE_IF_MORE, HANDLE_IF_IF)(expr)
#define HANDLE_IF_MORE(expr) CONCAT(COND_IF_,expr))
#define HANDLE_IF_TYPENAME(expr) DOIFTYPENAME: expr
#define HANDLE_IF_IF(expr) DOIF: expr
#define HANDLE_TRY(expr) DOTRY: expr
#define PARSE_COND(expr) CONCAT(COND_,expr))
PARSE_COND(if (A)(B)) => DOIF: (A)(B)
PARSE_COND(try (A)(B)) => DOTRY: (A)(B)
PARSE_COND(if typename (A)(B)) => DOIFTYPENAME: (A)(B)
1.2 构建 SFINAE 表达式
接下来,我们需要将实际表达式转换为有效的 C++ SFINAE 代码,以进行 3 种不同类型的检查。
我们将检查注入返回类型,例如:
template<class L, class R>
auto operator+(L const&, R const&) -> decltype(<check A>, <check B>, <actual deduced return type>) {
/* ... */
}
- 对于
if,我们可以使用std::enable_if:
expr -> typename std::enable_if<expr::value>::type()
如果expr 为真,这将导致void(),如果expr 为假,则产生替换错误
- 对于
try,我们可以保持表达式不变。
- 对于
if typename,我们可以使用std::declval:
expr -> std::declval<typename expr>()
我们需要使用std::declval,因为expr 可能不是默认可构造的。
因此,我们现在可以通过一个小的 foreach 宏将所有 3 种类型的 SFINAE 条件转换为 C++ 代码:godbolt
#define SEQ_HEAD(seq) EXPAND(DEFER(SEQ_HEAD_IMPL)(SEQ_HEAD_EL seq))
#define SEQ_HEAD_EL(el) el,
#define SEQ_HEAD_IMPL(head, tail) head
#define SEQ_TAIL(seq) SEQ_TAIL_IMPL seq
#define SEQ_TAIL_IMPL(el)
#define FOR_EACH(func, seq) IIF(HAS_MORE_TOKENS(seq), FOR_EACH_END, FOR_EACH_0)(func, seq)
#define FOR_EACH_END(...)
#define FOR_EACH_0(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_1)(func, SEQ_TAIL(seq))
#define FOR_EACH_1(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_2)(func, SEQ_TAIL(seq))
#define FOR_EACH_2(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_3)(func, SEQ_TAIL(seq))
#define FOR_EACH_3(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_4)(func, SEQ_TAIL(seq))
#define FOR_EACH_4(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_END)(func, SEQ_TAIL(seq))
#define HANDLE_IF_TYPENAME(expr) FOR_EACH(HANDLE_IF_TYPENAME_IMPL, expr)
#define HANDLE_IF_TYPENAME_IMPL(expr) std::declval<typename expr>(),
#define HANDLE_IF_IF(expr) FOR_EACH(HANDLE_IF_IF_IMPL, expr)
#define HANDLE_IF_IF_IMPL(expr) typename std::enable_if<expr::value>::type(),
#define HANDLE_TRY(expr) FOR_EACH(HANDLE_TRY_IMPL, expr)
#define HANDLE_TRY_IMPL(expr) expr,
PARSE_COND(if (A)(B)) => typename std::enable_if<A::value>::type(), typename std::enable_if<B::value>::type(),
PARSE_COND(try (A)(B)) => A, B,
PARSE_COND(if typename (A)(B)) => std::declval<typename A>(), std::declval<typename B>(),
2。大楼BOOST_AUTO_FUNCTION
现在我们可以解析条件,我们几乎拥有了获得BOOST_AUTO_FUNCTION 的实际实现所需的一切。
我们只需要另一个FOR_EACH 实现来循环传递给BOOST_AUTO_FUNCTION 的不同条件列表,以及更多的宏:
#define FOR_EACH_I(func, seq) IIF(HAS_MORE_TOKENS(seq), FOR_EACH_I_END, FOR_EACH_I_0)(func, seq)
#define FOR_EACH_I_END(...)
#define FOR_EACH_I_0(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_1)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_1(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_2)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_2(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_3)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_3(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_4)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_4(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_END)(func, SEQ_TAIL(seq))
#define TUP_SIZE(...) TUP_SIZE_IMPL(,##__VA_ARGS__,3,2,1,0)
#define TUP_SIZE_IMPL(a,b,c,d,e,...) e
#define TUP_TO_SEQ(...) CONCAT(TUP_TO_SEQ_, TUP_SIZE(__VA_ARGS__))(__VA_ARGS__)
#define TUP_TO_SEQ_0()
#define TUP_TO_SEQ_1(a) (a)
#define TUP_TO_SEQ_2(a,b) (a)(b)
#define TUP_TO_SEQ_3(a,b,c) (a)(b)(c)
#define HANDLE_CONDS(...) FOR_EACH_I(PARSE_COND, TUP_TO_SEQ(__VA_ARGS__))
#define INFER_RETURN_TYPE(...) ([&]() { __VA_ARGS__; })()
#define BUILD_FUNC_BODY(...) INFER_RETURN_TYPE(__VA_ARGS__)) { __VA_ARGS__; }
#define BOOST_AUTO_FUNCTION(signature, ...) auto signature -> decltype( HANDLE_CONDS(__VA_ARGS__) BUILD_FUNC_BODY
就是这样——现在我们已经实现了BOOST_AUTO_FUNCTION:godbolt
template< class L, class R >
BOOST_AUTO_FUNCTION( operator -( L const& lhs, R const& rhs )
, if ( is_vector_udt< L > )
( is_vector_udt< R > )
, try ( lhs + rhs )
( -rhs )
, if typename ( L::value_type )
)
(
return lhs + -rhs
)
template< class L, class R >
auto operator -( L const& lhs, R const& rhs ) -> decltype(
typename std::enable_if<is_vector_udt< L >::value>::type(),
typename std::enable_if<is_vector_udt< R >::value>::type(),
lhs + rhs,
-rhs,
std::declval<typename L::value_type>(),
([&]() { return lhs + -rhs; })()) // <- problem
{
return lhs + -rhs;
}
但有一个问题 - lambdas 不能在未评估的上下文中使用(在 C++20 中它们现在可以使用,但只能在没有捕获子句的情况下使用)。
所以不幸的是,这并没有按原样编译。
对此也没有简单的解决方法(至少在 C++11 中 - 在 C++14 中我们可以使用自动返回类型推导) - 所以不幸的是,BOOST_AUTO_FUNCTION 无法按原样实现C++11。
3。让它发挥作用
使BOOST_AUTO_FUNCTION 在 C++11 中可行的一种方法是从列表中删除最后一个功能:
- 从给定的正文推导出返回类型;
如果我们例如向BOOST_AUTO_FUNCTION 添加一个额外的参数,专门用于返回类型的推断:
template< class L, class R >
BOOST_AUTO_FUNCTION( operator -( L const& lhs, R const& rhs ),
lhs + -rhs // <- this will be used for return-type deduction
, if ( is_vector_udt< L > )
( is_vector_udt< R > )
, try ( lhs + rhs )
( -rhs )
, if typename ( L::value_type )
)
(
return lhs + -rhs
)
然后我们只需要稍微修改我们现有的宏就可以了:godbolt
#define HANDLE_CONDS(...) FOR_EACH_I(PARSE_COND, TUP_TO_SEQ(__VA_ARGS__))
#define BUILD_FUNC_BODY(...) { __VA_ARGS__; }
#define BOOST_AUTO_FUNCTION(signature, return_type, ...) auto signature -> decltype( HANDLE_CONDS(__VA_ARGS__) return_type) BUILD_FUNC_BODY
现在我们实际上有了一个有效的BOOST_AUTO_FUNCTION 实现!
这是上面示例将生成的代码:godbolt
template< class L, class R >
auto operator -( L const& lhs, R const& rhs ) -> decltype(
typename std::enable_if<is_vector_udt< L >::value>::type(),
typename std::enable_if<is_vector_udt< R >::value>::type(),
lhs + rhs,
-rhs,
std::declval<typename L::value_type>(),
lhs + -rhs // <- expression from our additional parameter
) {
return lhs + -rhs;
}
这是我们稍作修改的BOOST_AUTO_FUNCTION:godbolt的完整实现
#define CONCAT_IMPL(a, b) a ## b
#define CONCAT(a,b) CONCAT_IMPL(a, b)
#define EXPAND(...) __VA_ARGS__
#define EMPTY()
#define DEFER(id) id EMPTY()
#define HAS_MORE_TOKENS(expr) EXPAND(DEFER(HAS_MORE_TOKENS_RESULT)(HAS_MORE_TOKENS_CHECK expr, 0, 1))
#define HAS_MORE_TOKENS_CHECK(...) ~,~
#define HAS_MORE_TOKENS_RESULT(a, b, c, ...) c
#define IIF(condition, a, b) CONCAT(IIF_, condition)(a, b)
#define IIF_0(a, b) b
#define IIF_1(a, b) a
#define SEQ_HEAD(seq) EXPAND(DEFER(SEQ_HEAD_IMPL)(SEQ_HEAD_EL seq))
#define SEQ_HEAD_EL(el) el,
#define SEQ_HEAD_IMPL(head, tail) head
#define SEQ_TAIL(seq) SEQ_TAIL_IMPL seq
#define SEQ_TAIL_IMPL(el)
#define FOR_EACH(func, seq) IIF(HAS_MORE_TOKENS(seq), FOR_EACH_END, FOR_EACH_0)(func, seq)
#define FOR_EACH_END(...)
#define FOR_EACH_0(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_1)(func, SEQ_TAIL(seq))
#define FOR_EACH_1(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_2)(func, SEQ_TAIL(seq))
#define FOR_EACH_2(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_3)(func, SEQ_TAIL(seq))
#define FOR_EACH_3(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_4)(func, SEQ_TAIL(seq))
#define FOR_EACH_4(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_END, FOR_EACH_END)(func, SEQ_TAIL(seq))
#define FOR_EACH_I(func, seq) IIF(HAS_MORE_TOKENS(seq), FOR_EACH_I_END, FOR_EACH_I_0)(func, seq)
#define FOR_EACH_I_END(...)
#define FOR_EACH_I_0(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_1)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_1(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_2)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_2(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_3)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_3(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_4)(func, SEQ_TAIL(seq))
#define FOR_EACH_I_4(func, seq) func(SEQ_HEAD(seq)) IIF(HAS_MORE_TOKENS(SEQ_TAIL(seq)), FOR_EACH_I_END, FOR_EACH_I_END)(func, SEQ_TAIL(seq))
#define TUP_SIZE(...) TUP_SIZE_IMPL(,##__VA_ARGS__,3,2,1,0)
#define TUP_SIZE_IMPL(a,b,c,d,e,...) e
#define TUP_TO_SEQ(...) CONCAT(TUP_TO_SEQ_, TUP_SIZE(__VA_ARGS__))(__VA_ARGS__)
#define TUP_TO_SEQ_0()
#define TUP_TO_SEQ_1(a) (a)
#define TUP_TO_SEQ_2(a,b) (a)(b)
#define TUP_TO_SEQ_3(a,b,c) (a)(b)(c)
#define COND_if HANDLE_IF (
#define COND_try HANDLE_TRY (
#define COND_IF_typename HANDLE_IF_TYPENAME (
#define HANDLE_IF(expr) IIF(HAS_MORE_TOKENS(expr), HANDLE_IF_MORE, HANDLE_IF_IF)(expr)
#define HANDLE_IF_MORE(expr) CONCAT(COND_IF_,expr))
#define HANDLE_IF_TYPENAME(expr) FOR_EACH(HANDLE_IF_TYPENAME_IMPL, expr)
#define HANDLE_IF_TYPENAME_IMPL(expr) std::declval<typename expr>(),
#define HANDLE_IF_IF(expr) FOR_EACH(HANDLE_IF_IF_IMPL, expr)
#define HANDLE_IF_IF_IMPL(expr) typename std::enable_if<expr::value>::type(),
#define HANDLE_TRY(expr) FOR_EACH(HANDLE_TRY_IMPL, expr)
#define HANDLE_TRY_IMPL(expr) expr,
#define PARSE_COND(expr) CONCAT(COND_,expr))
#define HANDLE_CONDS(...) FOR_EACH_I(PARSE_COND, TUP_TO_SEQ(__VA_ARGS__))
#define BUILD_FUNC_BODY(...) { __VA_ARGS__; }
#define BOOST_AUTO_FUNCTION(signature, return_type, ...) auto signature -> decltype( HANDLE_CONDS(__VA_ARGS__) return_type) BUILD_FUNC_BODY
// Usage:
template< class L, class R >
BOOST_AUTO_FUNCTION( operator -( L const& lhs, R const& rhs ),
lhs + -rhs
, if ( is_vector_udt< L > )
( is_vector_udt< R > )
, try ( lhs + rhs )
( -rhs )
, if typename ( L::value_type )
)
(
return lhs + -rhs
)
使用 Boost 预处理器,我们可以减少大量样板宏代码。
这是使用 boost pp 的相同实现的外观:godbolt
#include <boost/preprocessor.hpp>
#define COND_if HANDLE_IF (
#define COND_try HANDLE_TRY (
#define COND_IF_typename HANDLE_IF_TYPENAME (
#define HANDLE_IF(expr) BOOST_PP_IIF(BOOST_PP_IS_BEGIN_PARENS(expr), HANDLE_IF_IF, HANDLE_IF_MORE)(expr)
#define HANDLE_IF_MORE(expr) BOOST_PP_CAT(COND_IF_,expr))
#define HANDLE_IF_TYPENAME(expr) BOOST_PP_SEQ_FOR_EACH(HANDLE_IF_TYPENAME_IMPL, ~, expr)
#define HANDLE_IF_TYPENAME_IMPL(r, _, expr) std::declval<typename expr>(),
#define HANDLE_IF_IF(expr) BOOST_PP_SEQ_FOR_EACH(HANDLE_IF_IF_IMPL, ~, expr)
#define HANDLE_IF_IF_IMPL(r, _, expr) typename std::enable_if<expr::value>::type(),
#define HANDLE_TRY(expr) BOOST_PP_SEQ_FOR_EACH(HANDLE_TRY_IMPL, ~, expr)
#define HANDLE_TRY_IMPL(r, _, expr) expr,
#define PARSE_COND(r, _, i, expr) BOOST_PP_CAT(COND_,expr))
#define TUP_TO_SEQ(...) BOOST_PP_TUPLE_TO_SEQ((__VA_ARGS__))
#define HANDLE_CONDS(...) BOOST_PP_SEQ_FOR_EACH_I( PARSE_COND, ~, TUP_TO_SEQ(__VA_ARGS__))
#define BUILD_FUNC_BODY(...) { __VA_ARGS__; }
#define BOOST_AUTO_FUNCTION(signature, return_type, ...) auto signature -> decltype( HANDLE_CONDS(__VA_ARGS__) return_type) BUILD_FUNC_BODY
4。其他资源