#include <iostream>
#include <type_traits>

using namespace std;


#define DETECTED_MEMBER(member_name)                                                      \
template<typename C, typename T>class has_##member_name                                   \
{                                                                                         \
    template<class U> static constexpr auto Check(U*) -> typename                         \
    std::is_same< decltype( std::declval<U>().member_name), T >::type;                    \
                                                                                          \
    template<typename> static constexpr std::false_type Check(...);                       \
    typedef decltype(Check<C>(0)) Result;                                                 \
public:                                                                                   \
    enum { eIS_NOCONST = 0 };                                                             \
    enum { eIS_CONST   = 0 };                                                             \
    enum { eIS_AVAILABLE = Result::value };                                               \
};                                                                                        \
template<class C,typename R, typename... Args>class has_##member_name<C, R(Args...)>      \
{                                                                                         \
    typedef R(C::*Method)(Args...);    typedef R(C::*CMethod)(Args...)const;              \
    template <class U> using NoConst = decltype(static_cast<Method> (&U::member_name));   \
    template <class U> using Const   = decltype(static_cast<CMethod>(&U::member_name));   \
                                                                                          \
    template<class U> static constexpr std::true_type  CheckNoConst( NoConst<U>*p );      \
    template<class>   static constexpr std::false_type CheckNoConst(...);                 \
    typedef decltype(CheckNoConst<C>(0)) NoConstResult;                                   \
                                                                                          \
    template<class U> static constexpr std::true_type  CheckConst( Const<U>*p);           \
    template<class>   static constexpr std::false_type CheckConst(...);                   \
    typedef decltype(CheckConst<C>(0)) ConstResult;                                       \
public:                                                                                   \
    enum { eIS_NOCONST = NoConstResult::value      };                                     \
    enum { eIS_CONST   = ConstResult::value        };                                     \
    enum { eIS_AVAILABLE = eIS_NOCONST | eIS_CONST };                                     \
};

#define is_available(class_name, member_type, member_name)         has_##member_name<class_name, member_type>::eIS_AVAILABLE
#define is_available_method(class_name, member_type, member_name)  has_##member_name<class_name, member_type>::eIS_NOCONST
#define is_available_cmethod(class_name, member_type, member_name) has_##member_name<class_name, member_type>::eIS_CONST

//------------------------------------------------------------------------------------


int g;
struct A
{
    A():r(g){}

    void fooA(int);
    void fooA(int)const;
    void fooA();
    void fooA()const;

    int v;
    int& r;
    int a[10];
    int* p;
    int** pp;
private:
    void private_foo();
};

struct B : A
{
    int v;
    int fooB(){ return 0; }
};


DETECTED_MEMBER(fooA);
DETECTED_MEMBER(fooB);
DETECTED_MEMBER(v);
DETECTED_MEMBER(r);
DETECTED_MEMBER(a);
DETECTED_MEMBER(p);
DETECTED_MEMBER(pp);

DETECTED_MEMBER(private_foo);


int main()
{
    //синтаксис:  is_available(  имя_класса ,  тип_мембера  , имя_мембера)

    //вернет true если мембер доступен, иначе - false

    //---- детектирование данных
    cout<< "is available int B.v ? "
        << is_available(B,int,v)     <<endl;

    cout<< "is available int& B.r ? "
        << is_available(B,int&,r)    <<endl;

    cout<< "is available int B.a[10] ? "
        << is_available(B,int[10],a) <<endl;

    cout<< "is available int* B.p ? "
        << is_available(B,int*,p)    <<endl;

    cout<< "is available int* B.pp ? "
        << is_available(B,int**,pp)  <<endl;

    //---- детектирование методов
    cout<< "is available void B.fooA(int) or void B::fooA(int)const ? "
        <<is_available(B,void(int),fooA) <<endl;

    cout<< "is available void B.fooA(int) ? "
        << is_available_method(B,void(int),fooA) <<endl;

    cout<< "is available void B.fooA(int)const ? "
        << is_available_cmethod(B,void(int),fooA) <<endl;

    cout<< "is available int B.fooB() ? "
        << is_available_method(B,int(),fooB) <<endl;

    cout<< "is available void B.private_foo() ? "      
        << is_available(B,void(),private_foo) <<endl;
}


