首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >invocable_traits

invocable_traits
EN

Code Review用户
提问于 2022-01-05 15:17:00
回答 1查看 59关注 0票数 3

更新:这段代码有新版本:v3在这里发布v4在这里发布

目标:实现任何可调用的特性,返回其对称性、返回类型和参数类型。由于指向数据成员的指针也是可调用的,因此应该对这些指针进行处理(并将其视为0-偶然性)。

代码如下,试试这里。这是v2,在收到这里评论之后。除了建议的更改之外,我还添加了对函数引用的支持,现在还提供了有关可调用类的信息(如果是成员函数或数据成员),如果您想要获得不可调用的特性,则提供更好的错误消息。我决定不把引用作为第一个参数类型放到类中,而是单独提供它。测试class_type是否无效将告诉您是否需要对类实例进行引用。实际上,对于指向静态成员函数的指针,class_type是无效的。

代码语言:javascript
复制
#pragma once

#include <cstddef>
#include <tuple>
#include <type_traits>

// derived and extended from https://github.com/kennytm/utils/blob/master/traits.hpp
// and https://stackoverflow.com/a/28213747
// This does not handle overloaded functions (which includes functors with
// overloaded operator()), because the compiler would not be able to resolve
// the overload without knowing the argument types and the cv- and noexcept-
// qualifications. If you do know those already and can thus specify the
// overload to the compiler, you do not need this class. The only remaining
// piece of information is the result type, which you can get with
// std::invoke_result.

namespace detail
{
    template <std::size_t i, typename... Args>
    struct invocable_traits_arg_impl
    {
        static_assert(i<sizeof...(Args), "Requested argument type out of bounds (function does not declare this many arguments)");

        using type = std::tuple_element_t<i, std::tuple<Args...>>;
    };

    template <typename R, typename C, bool IsVariadic, typename... Args>
    struct invocable_traits_class
    {
        static constexpr std::size_t arity = sizeof...(Args);
        static constexpr auto is_variadic = IsVariadic;

        using result_type = R;
        using class_type = C;

        template <std::size_t i>
        using arg = invocable_traits_arg_impl<i,Args...>::type;
    };

    template <typename R, bool IsVariadic, typename... Args>
    struct invocable_traits_free : public invocable_traits_class<R, void, IsVariadic, Args...> {};
}

template <typename T>
struct invocable_traits;

#define INVOCABLE_TRAITS_SPEC(cv,...)                                               \
/* handle functions, with all possible iterations of reference and noexcept */      \
template <typename R, typename... Args>                                             \
struct invocable_traits<R(Args... __VA_OPT__(,) __VA_ARGS__) cv>                    \
    : public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {};          \
template <typename R, typename... Args>                                             \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv &>                   \
    : public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {};          \
template <typename R, typename... Args>                                             \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv &&>                  \
    : public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {};          \
template <typename R, typename... Args>                                             \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv noexcept>            \
    : public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {};          \
template <typename R, typename... Args>                                             \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv & noexcept>          \
    : public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {};          \
template <typename R, typename... Args>                                             \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv && noexcept>         \
    : public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {};          \
/* handle pointers to member functions (incl. iterations of reference and noexcept) */\
template <typename C, typename R, typename... Args>                                 \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv>               \
    : public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {};       \
template <typename C, typename R, typename... Args>                                 \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv &>             \
    : public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {};       \
template <typename C, typename R, typename... Args>                                 \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv &&>            \
    : public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {};       \
template <typename C, typename R, typename... Args>                                 \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv noexcept>      \
    : public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {};       \
template <typename C, typename R, typename... Args>                                 \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv & noexcept>    \
    : public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {};       \
template <typename C, typename R, typename... Args>                                 \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv && noexcept>   \
    : public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {};       \
/* handle pointers to data members */                                               \
__VA_OPT__( /* no variadic function version for data members, (inverted) skip */    \
template <typename C, typename R>                                                   \
struct invocable_traits<R C::* cv>                                                  \
    : public detail::invocable_traits_class<R,C,false> {};                          \
) /* end __VA_OPT___*/

// cover all const and volatile permutations
INVOCABLE_TRAITS_SPEC(, )
INVOCABLE_TRAITS_SPEC(const, )
INVOCABLE_TRAITS_SPEC(volatile, )
INVOCABLE_TRAITS_SPEC(const volatile, )
// and also variadic function versions
INVOCABLE_TRAITS_SPEC(, ...)
INVOCABLE_TRAITS_SPEC(const, ...)
INVOCABLE_TRAITS_SPEC(volatile, ...)
INVOCABLE_TRAITS_SPEC(const volatile, ...)
#undef INVOCABLE_TRAITS_SPEC

// pointers to functions
template <typename R, typename... Args>
struct invocable_traits<R(*)(Args...)>                  : public invocable_traits<R(Args...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(*)(Args...) noexcept>         : public invocable_traits<R(Args...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(*)(Args..., ...)>             : public invocable_traits<R(Args..., ...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(*)(Args..., ...) noexcept>    : public invocable_traits<R(Args..., ...)> {};
// references to functions
template <typename R, typename... Args>
struct invocable_traits<R(&)(Args...)>                  : public invocable_traits<R(Args...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(&)(Args...) noexcept>         : public invocable_traits<R(Args...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(&)(Args..., ...)>             : public invocable_traits<R(Args..., ...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(&)(Args..., ...) noexcept>    : public invocable_traits<R(Args..., ...)> {};

// get at operator() of any struct/class defining it (this includes lambdas)
// bit of machinery for better error messages
namespace detail {
    template <typename T>
    concept HasCallOperator = requires(T)
    {
        std::declval<T>().operator();
    };

    template <typename T, bool isCallable>
    struct invocable_traits_extract : invocable_traits<decltype(&T::operator())> {};

    template <typename T>
    struct invocable_traits_extract<T, false>
    {
        static_assert(std::is_class_v<T>, "passed type is not a class, and thus cannot have an operator()");
        static_assert(!std::is_class_v<T> || HasCallOperator<T>, "passed type is a class that doesn't have an operator()");
        
        // to reduce excessive compiler error output
        static constexpr std::size_t arity = 0;
        static constexpr auto is_variadic = false;
        using result_type = void;
        using class_type = void;
        template <size_t i> struct arg { using type = void; };
    };
}

template <typename T>
struct invocable_traits : detail::invocable_traits_extract<std::decay_t<T>, detail::HasCallOperator<T>> {};

测试代码:

代码语言:javascript
复制
#include <string>

void test(int)
{}
void test2(int) noexcept
{}
void testEllipsis(int,...)
{}

struct tester
{
    void yolo(char)
    {}
    void yoloEllipsis(char, ...)
    {}
    static long yoloStatic(short)
    {}

    void operator()(int in_)
    {}

    std::string field;
};


int main()
{
    auto lamb = [](const int& in_) {return "ret"; };

    using traits = invocable_traits<decltype(lamb)>;
    static_assert(std::is_same_v<const char*, traits::result_type>, "");
    static_assert(std::is_same_v<const int&, traits::arg<0>>, "");


    using traits2 = invocable_traits<decltype(&test)>;
    static_assert(std::is_same_v<void, traits2::result_type>, "");
    static_assert(std::is_same_v<int, traits2::arg<0>>, "");

    using traits2b = invocable_traits<decltype(&test2)>;
    static_assert(std::is_same_v<void, traits2b::result_type>, "");
    static_assert(std::is_same_v<int, traits2b::arg<0>>, "");
    static_assert(!traits2b::is_variadic, "");

    using traits2c = invocable_traits<decltype(&testEllipsis)>;
    static_assert(std::is_same_v<void, traits2c::result_type>, "");
    static_assert(std::is_same_v<int, traits2c::arg<0>>, "");
    static_assert(traits2c::is_variadic, "");

    using traits3 = invocable_traits<decltype(&tester::yolo)>;
    static_assert(std::is_same_v<void, traits3::result_type>, "");
    static_assert(std::is_same_v<char, traits3::arg<0>>, "");

    using traits3b = invocable_traits<decltype(&tester::yoloEllipsis)>;
    static_assert(std::is_same_v<void, traits3b::result_type>, "");
    static_assert(std::is_same_v<tester, traits3b::class_type>, "");
    static_assert(std::is_same_v<char, traits3b::arg<0>>, "");
    static_assert(traits3b::is_variadic, "");

    using traits3c = invocable_traits<decltype(&tester::yoloStatic)>;
    static_assert(std::is_same_v<long, traits3c::result_type>, "");
    static_assert(std::is_same_v<void, traits3c::class_type>, "");
    static_assert(std::is_same_v<short, traits3c::arg<0>>, "");
    static_assert(!traits3c::is_variadic, "");

    using traits4 = invocable_traits<decltype(&tester::field)>;
    static_assert(std::is_same_v<std::string, traits4::result_type>, "");
    static_assert(traits4::arity==0, "");


    using traits5 = invocable_traits<std::add_rvalue_reference_t<decltype(lamb)>>;
    static_assert(std::is_same_v<const char*, traits5::result_type>, "");
    static_assert(std::is_same_v<const int&, traits5::arg<0>>, "");

    using traits6 = invocable_traits<std::add_lvalue_reference_t<decltype(lamb)>>;
    static_assert(std::is_same_v<const char*, traits6::result_type>, "");
    static_assert(std::is_same_v<decltype(lamb), traits6::class_type>, "");
    static_assert(std::is_same_v<const int&, traits6::arg<0>>, "");

    auto lamb2 = [](const int& in_, ...) mutable noexcept {return "ret"; };

    // functor
    using traits7 = invocable_traits<tester>;
    static_assert(std::is_same_v<void, traits7::result_type>, "");
    static_assert(std::is_same_v<int, traits7::arg<0>>, "");


    using traits8 = invocable_traits<decltype(lamb2)>;
    static_assert(std::is_same_v<const char*, traits8::result_type>, "");
    static_assert(std::is_same_v<const int&, traits8::arg<0>>, "");
    static_assert(traits8::is_variadic, "");

    /*using traits9 = invocable_traits<int>;
    static_assert(std::is_same_v<const char*, traits9::result_type>, "");*/
    
  return 0;
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2022-01-05 22:40:12

一定要在宏使用后对其进行#undef。这是为了在标题中展开,而不是用户调用。

它对重载的函数有什么作用?

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/272668

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档