Check if a class has a member function of a given signature

后端 未结 17 1483
無奈伤痛
無奈伤痛 2020-11-22 03:00

I\'m asking for a template trick to detect if a class has a specific member function of a given signature.

The problem is similar to the one cited here http://www.go

相关标签:
17条回答
  • 2020-11-22 03:31

    Came with the same kind of problem myself, and found the proposed solutions in here very interesting... but had the requirement for a solution that:

    1. Detects inherited functions as well;
    2. Is compatible with non C++11 ready compilers (so no decltype)

    Found another thread proposing something like this, based on a BOOST discussion. Here is the generalisation of the proposed solution as two macros declaration for traits class, following the model of boost::has_* classes.

    #include <boost/type_traits/is_class.hpp>
    #include <boost/mpl/vector.hpp>
    
    /// Has constant function
    /** \param func_ret_type Function return type
        \param func_name Function name
        \param ... Variadic arguments are for the function parameters
    */
    #define DECLARE_TRAITS_HAS_FUNC_C(func_ret_type, func_name, ...) \
        __DECLARE_TRAITS_HAS_FUNC(1, func_ret_type, func_name, ##__VA_ARGS__)
    
    /// Has non-const function
    /** \param func_ret_type Function return type
        \param func_name Function name
        \param ... Variadic arguments are for the function parameters
    */
    #define DECLARE_TRAITS_HAS_FUNC(func_ret_type, func_name, ...) \
        __DECLARE_TRAITS_HAS_FUNC(0, func_ret_type, func_name, ##__VA_ARGS__)
    
    // Traits content
    #define __DECLARE_TRAITS_HAS_FUNC(func_const, func_ret_type, func_name, ...)  \
        template                                                                  \
        <   typename Type,                                                        \
            bool is_class = boost::is_class<Type>::value                          \
        >                                                                         \
        class has_func_ ## func_name;                                             \
        template<typename Type>                                                   \
        class has_func_ ## func_name<Type,false>                                  \
        {public:                                                                  \
            BOOST_STATIC_CONSTANT( bool, value = false );                         \
            typedef boost::false_type type;                                       \
        };                                                                        \
        template<typename Type>                                                   \
        class has_func_ ## func_name<Type,true>                                   \
        {   struct yes { char _foo; };                                            \
            struct no { yes _foo[2]; };                                           \
            struct Fallback                                                       \
            {   func_ret_type func_name( __VA_ARGS__ )                            \
                    UTILITY_OPTIONAL(func_const,const) {}                         \
            };                                                                    \
            struct Derived : public Type, public Fallback {};                     \
            template <typename T, T t>  class Helper{};                           \
            template <typename U>                                                 \
            static no deduce(U*, Helper                                           \
                <   func_ret_type (Fallback::*)( __VA_ARGS__ )                    \
                        UTILITY_OPTIONAL(func_const,const),                       \
                    &U::func_name                                                 \
                >* = 0                                                            \
            );                                                                    \
            static yes deduce(...);                                               \
        public:                                                                   \
            BOOST_STATIC_CONSTANT(                                                \
                bool,                                                             \
                value = sizeof(yes)                                               \
                    == sizeof( deduce( static_cast<Derived*>(0) ) )               \
            );                                                                    \
            typedef ::boost::integral_constant<bool,value> type;                  \
            BOOST_STATIC_CONSTANT(bool, is_const = func_const);                   \
            typedef func_ret_type return_type;                                    \
            typedef ::boost::mpl::vector< __VA_ARGS__ > args_type;                \
        }
    
    // Utility functions
    #define UTILITY_OPTIONAL(condition, ...) UTILITY_INDIRECT_CALL( __UTILITY_OPTIONAL_ ## condition , ##__VA_ARGS__ )
    #define UTILITY_INDIRECT_CALL(macro, ...) macro ( __VA_ARGS__ )
    #define __UTILITY_OPTIONAL_0(...)
    #define __UTILITY_OPTIONAL_1(...) __VA_ARGS__
    

    These macros expand to a traits class with the following prototype:

    template<class T>
    class has_func_[func_name]
    {
    public:
        /// Function definition result value
        /** Tells if the tested function is defined for type T or not.
        */
        static const bool value = true | false;
    
        /// Function definition result type
        /** Type representing the value attribute usable in
            http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html
        */
        typedef boost::integral_constant<bool,value> type;
    
        /// Tested function constness indicator
        /** Indicates if the tested function is const or not.
            This value is not deduced, it is forced depending
            on the user call to one of the traits generators.
        */
        static const bool is_const = true | false;
    
        /// Tested function return type
        /** Indicates the return type of the tested function.
            This value is not deduced, it is forced depending
            on the user's arguments to the traits generators.
        */
        typedef func_ret_type return_type;
    
        /// Tested function arguments types
        /** Indicates the arguments types of the tested function.
            This value is not deduced, it is forced depending
            on the user's arguments to the traits generators.
        */
        typedef ::boost::mpl::vector< __VA_ARGS__ > args_type;
    };
    

    So what is the typical usage one can do out of this?

    // We enclose the traits class into
    // a namespace to avoid collisions
    namespace ns_0 {
        // Next line will declare the traits class
        // to detect the member function void foo(int,int) const
        DECLARE_TRAITS_HAS_FUNC_C(void, foo, int, int);
    }
    
    // we can use BOOST to help in using the traits
    #include <boost/utility/enable_if.hpp>
    
    // Here is a function that is active for types
    // declaring the good member function
    template<typename T> inline
    typename boost::enable_if< ns_0::has_func_foo<T> >::type
    foo_bar(const T &_this_, int a=0, int b=1)
    {   _this_.foo(a,b);
    }
    
    // Here is a function that is active for types
    // NOT declaring the good member function
    template<typename T> inline
    typename boost::disable_if< ns_0::has_func_foo<T> >::type
    foo_bar(const T &_this_, int a=0, int b=1)
    {   default_foo(_this_,a,b);
    }
    
    // Let us declare test types
    struct empty
    {
    };
    struct direct_foo
    {
        void foo(int,int);
    };
    struct direct_const_foo
    {
        void foo(int,int) const;
    };
    struct inherited_const_foo :
        public direct_const_foo
    {
    };
    
    // Now anywhere in your code you can seamlessly use
    // the foo_bar function on any object:
    void test()
    {
        int a;
        foo_bar(a); // calls default_foo
    
        empty b;
        foo_bar(b); // calls default_foo
    
        direct_foo c;
        foo_bar(c); // calls default_foo (member function is not const)
    
        direct_const_foo d;
        foo_bar(d); // calls d.foo (member function is const)
    
        inherited_const_foo e;
        foo_bar(e); // calls e.foo (inherited member function)
    }
    
    0 讨论(0)
  • 2020-11-22 03:32

    If you are using facebook folly, their are out of the box macro to help you:

    #include <folly/Traits.h>
    namespace {
      FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test);
    } // unnamed-namespace
    
    void some_func() {
      cout << "Does class Foo have a member int test() const? "
        << boolalpha << has_test_traits<Foo, int() const>::value;
    }
    

    Though the implementation details is the same with the previous answer, use a library is simpler.

    0 讨论(0)
  • 2020-11-22 03:33

    You can use std::is_member_function_pointer

    class A {
       public:
         void foo() {};
    }
    
     bool test = std::is_member_function_pointer<decltype(&A::foo)>::value;
    
    0 讨论(0)
  • 2020-11-22 03:33

    I had a similar need and came across o this SO. There are many interesting/powerful solutions proposed here, though it is a bit long for just a specific need : detect if a class has member function with a precise signature. So I did some reading/testing and came up with my version that could be of interest. It detect :

    • static member function
    • non-static member function
    • non-static member function const

    with a precise signature. Since I don't need to capture any signature (that'd require a more complicated solution), this one suites to me. It basically used enable_if_t.

    struct Foo{ static int sum(int, const double&){return 0;} };
    struct Bar{ int calc(int, const double&) {return 1;} };
    struct BarConst{ int calc(int, const double&) const {return 1;} };
    
    // Note : second typename can be void or anything, as long as it is consistent with the result of enable_if_t
    template<typename T, typename = T> struct has_static_sum : std::false_type {};
    template<typename T>
    struct has_static_sum<typename T,
                            std::enable_if_t<std::is_same<decltype(T::sum), int(int, const double&)>::value,T> 
                          > : std::true_type {};
    
    template<typename T, typename = T> struct has_calc : std::false_type {};
    template<typename T>
    struct has_calc <typename T,
                      std::enable_if_t<std::is_same<decltype(&T::calc), int(T::*)(int, const double&)>::value,T>
                    > : std::true_type {};
    
    template<typename T, typename = T> struct has_calc_const : std::false_type {};
    template<typename T>
    struct has_calc_const <typename T,
                            std::enable_if_t<std::is_same<decltype(&T::calc), int(T::*)(int, const double&) const>::value,T>
                          > : std::true_type {};
    
    int main ()
    {
        constexpr bool has_sum_val = has_static_sum<Foo>::value;
        constexpr bool not_has_sum_val = !has_static_sum<Bar>::value;
    
        constexpr bool has_calc_val = has_calc<Bar>::value;
        constexpr bool not_has_calc_val = !has_calc<Foo>::value;
    
        constexpr bool has_calc_const_val = has_calc_const<BarConst>::value;
        constexpr bool not_has_calc_const_val = !has_calc_const<Bar>::value;
    
        std::cout<< "           has_sum_val " << has_sum_val            << std::endl
                 << "       not_has_sum_val " << not_has_sum_val        << std::endl
                 << "          has_calc_val " << has_calc_val           << std::endl
                 << "      not_has_calc_val " << not_has_calc_val       << std::endl
                 << "    has_calc_const_val " << has_calc_const_val     << std::endl
                 << "not_has_calc_const_val " << not_has_calc_const_val << std::endl;
    }
    

    Output :

               has_sum_val 1
           not_has_sum_val 1
              has_calc_val 1
          not_has_calc_val 1
        has_calc_const_val 1
    not_has_calc_const_val 1
    
    0 讨论(0)
  • 2020-11-22 03:34

    Here's a possible implementation relying on C++11 features. It correctly detects the function even if it's inherited (unlike the solution in the accepted answer, as Mike Kinghan observes in his answer).

    The function this snippet tests for is called serialize:

    #include <type_traits>
    
    // Primary template with a static assertion
    // for a meaningful error message
    // if it ever gets instantiated.
    // We could leave it undefined if we didn't care.
    
    template<typename, typename T>
    struct has_serialize {
        static_assert(
            std::integral_constant<T, false>::value,
            "Second template parameter needs to be of function type.");
    };
    
    // specialization that does the checking
    
    template<typename C, typename Ret, typename... Args>
    struct has_serialize<C, Ret(Args...)> {
    private:
        template<typename T>
        static constexpr auto check(T*)
        -> typename
            std::is_same<
                decltype( std::declval<T>().serialize( std::declval<Args>()... ) ),
                Ret    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            >::type;  // attempt to call it and see if the return type is correct
    
        template<typename>
        static constexpr std::false_type check(...);
    
        typedef decltype(check<C>(0)) type;
    
    public:
        static constexpr bool value = type::value;
    };
    

    Usage:

    struct X {
         int serialize(const std::string&) { return 42; } 
    };
    
    struct Y : X {};
    
    std::cout << has_serialize<Y, int(const std::string&)>::value; // will print 1
    
    0 讨论(0)
提交回复
热议问题