#include <iostream>
#include <boost/preprocessor.hpp>
#include <boost/callable_traits/is_invocable.hpp>
#define IS_VALID_EXPANDER_BEGIN(count) \
[](BOOST_PP_REPEAT(count, IS_VALID_EXPANDER_MIDDLE, \
_)) constexpr->decltype IS_VALID_EXPANDER_END
#define IS_VALID_EXPANDER_MIDDLE(z, idx, _) BOOST_PP_COMMA_IF(idx) auto _##idx
#define IS_VALID_EXPANDER_END(...) \
(__VA_ARGS__){})
#define IS_VALID(...) \
is_valid<__VA_ARGS__>(IS_VALID_EXPANDER_BEGIN( \
BOOST_PP_VARIADIC_SIZE(__VA_ARGS__))
template <typename... Ts, typename TF>
static constexpr auto is_valid(TF)
{
return boost::callable_traits::is_invocable<std::decay_t<TF>(Ts...), Ts...>{};
}
struct Test {};
int main()
{
std::cout << IS_VALID(std::ostream&, double)(_0 << _1) << std::endl;
std::cout << IS_VALID(std::ostream&, Test)(_0 << _1) << std::endl;
}
但是,结果是:
1
1
我不知道为什么。
答案 0 :(得分:1)
您的lambda按值接受参数,这不允许您测试std::ostream& << T
。将宏更改为:
#define IS_VALID_EXPANDER_BEGIN(count) \
[](BOOST_PP_REPEAT(count, IS_VALID_EXPANDER_MIDDLE, \
&&_)) constexpr->decltype IS_VALID_EXPANDER_END
另外,您对is_invocable
的使用是错误的-应该是
template <typename... Ts, typename TF>
static constexpr auto is_valid(TF)
{
return boost::callable_traits::is_invocable<std::decay_t<TF>, Ts...>{};
}
(无论如何,您都应该使用std::is_invocable
,它在C ++ 17中可用。)
答案 1 :(得分:1)
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
template<class F>
constexpr auto invokeable( F&& f ) {
return [](auto&&...args) {
return std::is_invocable< F&&, decltype(args)... >{};
};
}
现在我们可以用更少的宏魔术了:
std::cout << invokeable([](auto& lhs, auto&& v) RETURNS( lhs << v ))( std::cout, 0.0 ) << std::endl;
std::cout << invokeable([](auto& lhs, auto&& v) RETURNS( lhs << v ))( std::cout, Test{} ) << std::endl;
如果_0 << _1
构建了一个SFINAE友好的可调用对象(看起来如此),您甚至可以:
std::cout << invokeable(_0 << _1)( std::cout, Test{} ) << std::endl;
invokeable
是一个功能对象适配器。它将功能对象变成测试器,以查看传递给它的参数是否合法。
它确实要求您实际上具有正确类型的参数。您可以通过以下方法解决此问题:
template<class T>
struct tag_t { using type=T; };
template<class T>
constexpr tag_t<T> tag{};
template<class X>
struct untag { using type=X; };
template<class X>
using untag_t = typename untag<X>::type;
template<class T>
struct untag<tag_t<T>> { using type=T; };
template<class T>
struct untag<tag_t<T>&&> { using type=T; };
template<class T>
struct untag<tag_t<T>&> { using type=T; };
template<class T>
struct untag<tag_t<T>const &> { using type=T; };
然后修改:
template<class F>
constexpr auto invokeable( F&& f ) {
return [](auto&&...args) {
return std::is_invocable< F&&, untag_t<decltype(args)>... >{};
};
}
允许传递tag<std::ostream&>
而不是类型std::ostream&
的左值来测试所有std::ostream&
是否可以在该插槽中工作。
此处使用的唯一宏是RETURNS
,这使对SFINAE友好的lambda变得更容易。无论如何,有很多建议在c++20中添加与RETURNS
等效的内容。