Искал на нашем форуме 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м параметром именем |
| |