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

后端 未结 17 1453
無奈伤痛
無奈伤痛 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 
    #include 
    
    /// 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::value                          \
        >                                                                         \
        class has_func_ ## func_name;                                             \
        template                                                   \
        class has_func_ ## func_name                                  \
        {public:                                                                  \
            BOOST_STATIC_CONSTANT( bool, value = false );                         \
            typedef boost::false_type type;                                       \
        };                                                                        \
        template                                                   \
        class has_func_ ## func_name                                   \
        {   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   class Helper{};                           \
            template                                                  \
            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(0) ) )               \
            );                                                                    \
            typedef ::boost::integral_constant 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 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 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 
    
    // Here is a function that is active for types
    // declaring the good member function
    template inline
    typename boost::enable_if< ns_0::has_func_foo >::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 inline
    typename boost::disable_if< ns_0::has_func_foo >::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)
    }
    

提交回复
热议问题