代码之家  ›  专栏  ›  技术社区  ›  chila

C++ 17检查有效表达式

  •  5
  • chila  · 技术社区  · 7 年前

    this this ,我写了这个版本:

    #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
    

    我不明白为什么。

    2 回复  |  直到 7 年前
        1
  •  1
  •   Vittorio Romeo    7 年前

    您的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中是可用的。

    live example on wandbox.org

        2
  •  1
  •   Yakk - Adam Nevraumont    7 年前
    #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;
    

    Live example

    如果 _0 << _1

    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& 标准::ostream&

    Live example .

    这里使用的唯一宏是 RETURNS 退货 在里面