Determining if a type has a certain method??

Hi,

i am writing a template that detects whether a type has a certain method (call it Do). So far I have the following:

1
2
3
4
using bridge_type = void;       // can be ANY type!

template <class T>
using void_t = bridge_type;


// now test to see if Do() returns any type ==> thus proving its existence!!
1
2
3
4
5
6
7
8
9
template<class, typename = bridge_type>
struct has_Do : std::false_type
{};

template<class T>
struct has_Do<T, void_t< decltype(std::declval<T>().Do())>> : std::true_type
{
    
};

This works like a charm!!
But what if I wanted to check for any Do method with any number and type of arguments? This sounded to me like a place for variadics so I tried:

1
2
template< typename ...Ts>
using any_do_return = decltype(std::declval<A>().Do(Ts...rest));


but this does not compile!! I get
'Ts' does not refer to a value


Neither would it compile if I place it inside of the has_Do template (as a 2nd member for partial specialization).

Any ideas?

Regards,
Juan
Last edited on
What compiler errors do you get?

The only problem I see is rest, remove it:
1
2
template< typename ...Ts>
using any_do_return = decltype(std::declval<A>().Do(Ts...rest));


If I remove rest it continues giving the same error which is:

'Ts' does not refer to a value


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace detail
{
    template< typename T, typename... ARGS >
    using do_result = decltype( std::declval<T>().Do( std::declval<ARGS>()... ) ) ;

    template < typename T > struct has_mem_fun_Do
    {
        template < typename U, typename... A >static char test( U&&, do_result<U,A...>* = 0 ) ;
        using big = char[2] ; static big& test(...) ;

        static constexpr bool value = sizeof( std::declval<T>() ) == 1 ;
        using type = typename std::conditional< value, std::true_type, std::false_type >::type ;
    };
}

template < typename T > struct has_mem_fun_Do : detail::has_mem_fun_Do<T>::type {} ;

http://coliru.stacked-crooked.com/a/05b448eaf821d922
http://rextester.com/AYCBB9233
This does not work. The function test is never called so I think there is a typo...

This is ALWAYS true when T is any class:

sizeof( std::declval<T> ) == 1

Also ALWAYS true without std::declval<T> like so:

sizeof( T) == 1

Please take a look JLBorges. Thanks so much for posting - I am sure its ALMOST correct...

Juan
To correct m myself:


the expression is ALWAYS true for any class T which has NO non-static data members

-- However, this is still erroneous
What we need is the type returned by test!
Got the answer!! Just replace the original definition of value by:

static constexpr bool value = sizeof( test(std::declval<T>()) ) == 1 ;

Great!! Thanks JLBorges!!

Juan
I am intrigued by this template magic. It has been an amazing thought exercise. However, I don't think the final answer has been reached yet. I started with the code on the coliru page and tweaked it a little bit to better understand what's going on. Here are my code and my observations/questions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <type_traits>

namespace detail // containing @JUAN DENT's "fix"
{
    template< typename T, typename... ARGS >
    using do_result = decltype( std::declval<T>().Do( std::declval<ARGS>()... ) ) ;

    template < typename T > struct has_mem_fun_Do
    {
        template < typename U, typename... A >static char test( U&&, do_result<U,A...>* = 0 ) ;
        using big = char[2] ; static big& test(...) ;

        static constexpr bool value = sizeof( test(std::declval<T>()) ) == 1 ;
        using type = typename std::conditional< value, std::true_type, std::false_type >::type ;
    };
}

template < typename T > struct has_mem_fun_Do : detail::has_mem_fun_Do<T>::type {} ;

namespace detail2 // straight from @JLBorges
{
    template< typename T, typename... ARGS >
    using do_result = decltype( std::declval<T>().Do( std::declval<ARGS>()... ) ) ;

    template < typename T > struct has_mem_fun_Do
    {
        template < typename U, typename... A >static char test( U&&, do_result<U,A...>* = 0 ) ;
        using big = char[2] ; static big& test(...) ;

        static constexpr bool value = sizeof( std::declval<T>() ) == 1 ;
        using type = typename std::conditional< value, std::true_type, std::false_type >::type ;
    };
}
template < typename T > struct has_mem_fun_Do2 : detail2::has_mem_fun_Do<T>::type {} ;

int main()
{

    struct A { private: inline void DoNot(int) const noexcept {} };
    struct B { void Do(int) {} short Do(double,char) { return 0 ; } };
    struct C : B {};
    struct D { void DoNot() {} };
    
    std::cout << std::boolalpha;

    std::cout << "Using has_mem_fun_Do struct \n"; 
    std::cout << "A: " << has_mem_fun_Do<A>::value << '\n' ; // true
    std::cout << "B: " << has_mem_fun_Do<B>::value << '\n' ; // true
    std::cout << "C: " << has_mem_fun_Do<C>::value << '\n' ; // true
    std::cout << "D: " << has_mem_fun_Do<D>::value << '\n' ; // true
    std::cout << "stream: " << has_mem_fun_Do<std::ostream>::value << '\n' ; // false
    std::cout << std::endl;
    
    
    std::cout << "Using has_mem_fun_Do2 struct \n"; 
    std::cout << "A: " << has_mem_fun_Do2<A>::value << '\n' ; // true
    std::cout << "B: " << has_mem_fun_Do2<B>::value << '\n' ; // true
    std::cout << "C: " << has_mem_fun_Do2<C>::value << '\n' ; // true
    std::cout << "D: " << has_mem_fun_Do2<D>::value << '\n' ; // true
    std::cout << "stream: " << has_mem_fun_Do2<std::ostream>::value << '\n' ; // false
    std::cout << std::endl;

    std::cout << "Using detail2::has_mem_fun_Do struct \n"; 
    std::cout << "A: " << detail2::has_mem_fun_Do<A>::value << '\n' ; // true
    std::cout << "B: " << detail2::has_mem_fun_Do<B>::value << '\n' ; // true
    std::cout << "C: " << detail2::has_mem_fun_Do<C>::value << '\n' ; // true
    std::cout << "D: " << detail2::has_mem_fun_Do<D>::value << '\n' ; // true
    std::cout << "stream: " << detail2::has_mem_fun_Do<std::ostream>::value << '\n' ; // false
    std::cout << std::endl;
}


In namespace detail I tried @JUAN DENT's "fix" and nothing worked. Everything returns false. Juan's suggestion is not the answer.
Using has_mem_fun_Do struct 
A: false
B: false
C: false
D: false
stream: false


I created @JLBorges' original code in namespace detail2. I then added a new class D that does not contain a Do member function. D shows up at "true" with this code, so JLBorges' original posting is also not the complete answer.
Using has_mem_fun_Do2 struct 
A: true
B: true
C: true
D: true
stream: false


I was wondering what the true_type and false_type classes buy us. I'm not totally sold on the concept of true_type and false_type, so that may be clouding my vision here. But, the third set of output grab the value field from the struct in the namespace (instead of the value field from the struct outside of the namespace). The results are identical to using the struct outside the namespace. What is the extra layer of inheritance buying you?
Using detail2::has_mem_fun_Do struct 
A: true
B: true
C: true
D: true
stream: false
> JLBorges' original posting is also not the complete answer.

Yes, I am stupid. It is completely in the wrong direction, even after the typo is fixed.


> The results are identical to using the struct outside the namespace.
> What is the extra layer of inheritance buying you?

It doesn't buy much other than aligning with the facilities in <type_traits>
(provides for an implicit conversion to bool, and for use as a nullary compile-time predicate).
http://en.cppreference.com/w/cpp/types/integral_constant


C++17 proposal for detection idiom: http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4502.pdf

Boost TTI library: http://www.boost.org/doc/libs/1_61_0/libs/tti/doc/html/index.html

Brett Rossier (stackoverflow): http://stackoverflow.com/a/16867422

Microsoft extension __if_exists (run-time): https://msdn.microsoft.com/en-us/library/x7wy9xh3.aspx
Well, I am now baffled... As long as one Do() without parameters is present, the code works, but if any Do methods present all have parameters, then the code with my fix does not work...

What is the solution then JLBorges? Is there no way to fix your proposal?

Juan
Based on a very cursory examination, the elaborate mechanism proposed by Brett Rossier appears to work.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include <iostream>
#include <type_traits>


///////// Brett Rossier http://stackoverflow.com/a/16867422 //////////////////
/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

//Variadic to force ambiguity of class members.  C++11 and up.
template <typename... Args> struct ambiguate : public Args... {};

//Non-variadic version of the line above.
//template <typename A, typename B> struct ambiguate : public A, public B {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};

//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}

//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}

//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}

//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}

//Create all the checks for one member.  foobares NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)

///////// Brett Rossier http://stackoverflow.com/a/16867422 //////////////////

CREATE_MEMBER_CHECKS(foobar);


http://coliru.stacked-crooked.com/a/2bb2d97ac851a4b3
http://rextester.com/UHUM79338
@JLBorges,

Yes, I am stupid.


I hope that was tongue in cheek. You are anything but.

When I browse the forum and read one of your posts, I almost always come away impressed by the innovative solutions you suggest and the alternate ways you look at solving a problem. I don't think you get thanked enough by the forum denizens. I'll say it. Thank-you for the suggestions and solutions you post here on the board.

This one slipped through. Oh well. We'll live on.
Topic archived. No new replies allowed.