Есть ли метод у класса - HasMethod - старая тема
От: Molchalnik  
Дата: 08.11.19 22:11
Оценка:
Искал на нашем форуме SFINAE фишку по определению наличия класса в методе — нашёл только очень старую тему, написанную по старому стандарту, и которую крайне трудно адаптировать, потому что проще для каждого нового метода написать с нуля.

Решил обновить вопрос. Не претендую на новизну и оригинальность, наверняка кто-то где-то уже сделал так или почти так. Но на нашем форуме не нашёл, поэтому счёл уместным поднять вопрос снова.

Upd. 10.11.2019 по совету so5team переделал первоначальный вариант на type_traits версии c++17. первоначальный вариант — под катом

начнём с простого — зададим конкретный метод
  развернуть
struct A {
  //const char * AaBbCcDd(int i) { return "aa"; }
  void AaBbCcDd() {}
};
struct B {};



template <typename T, typename = void>
struct  HasMethod : public std::false_type {};
template <typename T >
struct HasMethod < T, std::void_t< decltype(&T::AaBbCcDd) > > : public std::true_type {};


int main() {
    printf("\n%u %u %u", HasMethod<A>::value, HasMethod<B>::value, HasMethod<int>::value );
    return 0;    
}

Но если надо будет сделать то же для другого метода, всё придётся переписывать. А кода много. Неудобно. Что делать? Нетрушные методы — макросы — помогут нам
#include <iostream>
#include <cstdio>


struct A {
  void AaBbCcDd() {}
};

struct B {
  const char * SomeFun(int i) { return "aa"; }
  };

/// @brief создаёт структурку ClassName с помощью которого можно определить наличие метода FunName в заданном классе (класс задаётся как параметр шаблона)
/// @details пример: 
///           HAS_METHOD_DEFINTION( HasPrintMethod, Print )
///           struct S { void Print() {} };
///           void main () {
///             bool exist = HasPrintMethod<S>::value;
///           }
/// @warning не работает, если в методе есть 2 перегрузки метода, наличие которого мы проверяем
#define HAS_METHOD_DEFINITION( ClassName, FunName ) \
template <typename T, typename = void> struct  ClassName : public std::false_type {}; \
template <typename T > struct ClassName < T, std::void_t< decltype(&T::FunName) > > : public std::true_type {};

HAS_METHOD_DEFINITION( HasMethod, AaBbCcDd )
HAS_METHOD_DEFINITION( HasMethod2, SomeFun )


int main() {
    printf("\n%u %u %u", HasMethod <A>::value, HasMethod <B>::value, HasMethod <int>::value );
    printf("\n%u %u %u", HasMethod2<A>::value, HasMethod2<B>::value, HasMethod2<int>::value );
    return 0;    
}


А вот вариант от rg45, который учитывает аргументы функции
Иногда он будет много полезнее моего, особенно в случае наличия перегруженных функций.
  Вариант rg45, реагирующий только на функцию с точно совпадающими аргументами
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>

#include <iostream>
#include <cstdio>


struct A {
  void AaBbCcDd() {}
};

struct B {
  const char * SomeFun(int i) { return "aa"; }
  };

/// @brief создаёт структурку ClassName с помощью которого можно определить наличие метода FunName в заданном классе (класс задаётся как параметр шаблона)
/// @details учитывает полностью аргументы функции
///          пример: 
///           HAS_METHOD_DEFINTION( HasPrintMethod, Print )
///           struct S { void Print( int, char, long) {} };
///           struct B { void NotPrint() {} };
///           void main () {
///             bool exist;
///             exist = HasPrintMethod<S, int, char, long>::value; // exist = 1
///             exist = HasPrintMethod<S, int, char>::value;       // exist = 0
///             exist = HasPrintMethod<B, int, char, long>::value; // exist = 0
///           }
/// @author rg45 aka Сергей Романченко в оформлении Molchalnik
/// @warning не работает, если в методе есть 2 перегрузки метода, наличие которого мы проверяем
#define HAS_METHOD_DEFINITION( ClassName, FunName )  \
    namespace curicios_hasmethod_namespace_ck9fs98 { \
    template <typename, typename = void>             \
    struct ClassName##_Helper : std::false_type {}; \
                                                     \
    template <typename Obj, typename...Args>             \
    struct ClassName##_Helper<std::tuple<Obj, Args...>, \
        std::void_t<decltype(std::declval<Obj>().FunName(std::declval<Args>()...))>> : std::true_type {}; \
    }  \
    template <typename Obj, typename...Args> using ClassName = curicios_hasmethod_namespace_ck9fs98::ClassName##_Helper<std::tuple<Obj, Args...> >; \
    
        

HAS_METHOD_DEFINITION( HasMethod, AaBbCcDd )
HAS_METHOD_DEFINITION( HasMethod2, SomeFun )


int main() {
    printf("\n%u %u %u", HasMethod <A>::value, HasMethod <B>::value, HasMethod <int>::value );
    printf("\n%u %u %u", HasMethod2<A, int>::value, HasMethod2<B, int>::value, HasMethod2<int, int>::value );
    return 0;    
}


  Первоначальный вариант
начнём с простого — зададим конкретный метод
#include <iostream>
#include <string>
#include <vector>
#include <cstdio>


struct A {
  //const char * AaBbCcDd(int i) { return "aa"; }
  void AaBbCcDd() {}
};

struct B {};


template <typename T> struct HasMethod {
 private:
  struct HasMethodHelperFalse {
    static constexpr const bool value = 0;
  };

  template <class T2, decltype(&T2::AaBbCcDd) U = &T2::AaBbCcDd > struct HasMethodHelperTrue {
    static constexpr const bool value = 1;
  };

  HasMethodHelperFalse static constexpr detect(...) { return HasMethodHelperFalse(); }

  template <typename T2> HasMethodHelperTrue<T2> static constexpr detect( T2 * ) { return HasMethodHelperTrue<T2>();  }

 public:
  static constexpr const bool value = decltype(     detect(   static_cast<T*>( nullptr )   )     ):: value;
};


int main() {
    printf("\n%u %u %u", HasMethod<A>::value, HasMethod<B>::value, HasMethod<int>::value );
    return 0;    
}


Но если надо будет сделать то же для другого метода, всё придётся переписывать. А кода много. Неудобно. Что делать? Нетрушные методы — макросы — помогут нам
#include <iostream>
#include <string>
#include <vector>
#include <cstdio>


struct A {
  const char * SomeFun(int i) { return "aa"; }
  void AaBbCcDd() {}
};

struct B {};

/// @brief создаёт структурку ClassName с помощью которого можно определить наличие метода FunName в заданном классе (класс задаётся как параметр шаблона)
/// @details пример: 
///           HAS_METHOD_DEFINTION( HasPrintMethod, Print )
///           struct S { void Print() {} };
///           void main () {
///             bool exist = HasPrintMethod<S>::value;
///           }
/// @warning не работает, если в методе есть 2 перегрузки метода, наличие которого мы проверяем
#define HAS_METHOD_DEFINITION( ClassName, FunName ) \
template <typename T> struct ClassName { \
 private: \
  struct HasMethodHelperFalse { \
    static constexpr const bool value = 0; \
  };\
\
  template <class T2, decltype(&T2::FunName) U = &T2:: FunName > struct HasMethodHelperTrue { \
    static constexpr const bool value = 1; \
  }; \
\
  HasMethodHelperFalse static constexpr detect(...) { return HasMethodHelperFalse(); } \
\
  template <typename T2> HasMethodHelperTrue<T2> static constexpr detect( T2 * ) { return HasMethodHelperTrue<T2>();  } \
\
 public: \
  static constexpr const bool value = decltype(     detect(   static_cast<T*>( nullptr )   )     ):: value;\
};

HAS_METHOD_DEFINITION( HasMethod, AaBbCcDd )
HAS_METHOD_DEFINITION( HasMethod2, SomeFun )


int main() {
    printf("\n%u %u %u", HasMethod <A>::value, HasMethod <B>::value, HasMethod <int>::value );
    printf("\n%u %u %u", HasMethod2<A>::value, HasMethod2<B>::value, HasMethod2<int>::value );
    return 0;    
}

Теперь, задав один макрос, мы получаем структурку с заданным 1м параметром именем, позволяющую нам определить наличие у любого класса метода с заданным 2м параметром именем
Отредактировано 22.11.2019 22:27 Molchalnik . Предыдущая версия . Еще …
Отредактировано 10.11.2019 9:20 Molchalnik . Предыдущая версия .
Отредактировано 09.11.2019 11:25 Molchalnik . Предыдущая версия .
Отредактировано 08.11.2019 22:17 Molchalnik . Предыдущая версия .
sfinae has method определить есть ли метод у класса
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.