Информация об изменениях

Сообщение Есть ли метод у класса - HasMethod - старая тема от 08.11.2019 22:11

Изменено 10.11.2019 9:20 Molchalnik

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

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

начнём с простого — зададим конкретный метод
#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м параметром именем
sfinae has method определить есть ли метод у класса
Есть ли метод у класса - HasMethod - старая тема
Искал на нашем форуме 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;    
}


  Первоначальный вариант
начнём с простого — зададим конкретный метод
#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м параметром именем
sfinae has method определить есть ли метод у класса