Искал на нашем форуме 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 * 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м параметром именем
Здравствуйте, Molchalnik, Вы писали:
M>начнём с простого — зададим конкретный метод
Гораздо более простой способ описан на cppreference. Для C++17 разработчику ничего и не нужно делать, std::void_t уже в наличии. Для C++14 void_t нужно будет определить самостоятельно, но там же на cppreference показано как это сделать.
Re[2]: Есть ли метод у класса - HasMethod - старая тема
Здравствуйте, Molchalnik, Вы писали:
M>Теперь, задав один макрос, мы получаем структурку с заданным 1м параметром именем, позволяющую нам определить наличие у любого класса метода с заданным 2м параметром именем
Из-за того, через какую задницу в "современном C++" приходится делать подобные вещи, и возникают сомнения в психическом здоровье главных идеологов языка, прямо пропагандирующих такой подход. По-хорошему, все это должно быть в ядре, а шаблоны должны использоваться для того, для чего были придуманы изначально.
Неудивительно, что у С++ все больше противников. Ну какое мнение об адекватности языка и его парадигм может возникнуть у человека, не являющегося фанатом C++, при виде подобного?
Re[2]: Есть ли метод у класса - HasMethod - старая тема
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Неудивительно, что у С++ все больше противников. Ну какое мнение об адекватности языка и его парадигм может возникнуть у человека, не являющегося фанатом C++, при виде подобного?
Не нравится этот рубанок — стругай другим. Оценка же рубанков весьма необъективна. Плюсы на данный момент — универсальный язык, который может всё, но из-за попытки натянуть сову на глобус с поддержкой Legacy содержащий всё больше и больше костылей. Рано или поздно он либо получит тег вроде version 23 { }, в рамках которого зашлёт всё легаси лесом и устроит новые продуманные синтаксис и правила. Либо кто-то предоставит язык, с возможностями плюсов, но без legacy. Пока другого универсального языка нет, а есть нишевые — которые теснят плюсы, но теснят скопом, все на одного. Никакой замены или альтернативы плюсам нет.
Re: Есть ли метод у класса - HasMethod - старая тема
Здравствуйте, Molchalnik, Вы писали:
M>Искал на нашем форуме SFINAE фишку по определению наличия класса в методе — нашёл только очень старую тему, написанную по старому стандарту, и которую крайне трудно адаптировать, потому что проще для каждого нового метода написать с нуля.
Здравствуйте, Molchalnik, Вы писали:
M>Искал на нашем форуме SFINAE фишку по определению наличия класса в методе — нашёл только очень старую тему, написанную по старому стандарту, и которую крайне трудно адаптировать, потому что проще для каждого нового метода написать с нуля.
Тут уже упомянули cppreference, но ссылку дали не туда. Вот, насколько я понял, прямо то что нужно. Ну и пример оттуда, немного модифицированный
#include <type_traits>
class A {
public:
void member1() { }
void member2() { }
};
class B
{
};
template<typename T>
using HasMember1 = std::is_member_function_pointer<decltype(&T::member1)>;
template<typename T>
using HasMember2 = std::is_member_function_pointer<decltype(&T::member2)>;
int main()
{
static_assert(std::is_member_function_pointer_v<decltype(&A::member1)>,
"A::membe1r is not a member function.");
static_assert(std::is_member_function_pointer_v<decltype(&A::member2)>,
"A::member2 is not a member function.");
static_assert(HasMember1<A>::value);
static_assert(HasMember2<A>::value);
//static_assert(HasMember1<B>::value); // uncommment and get compilation error
//static_assert(HasMember1<int>::valu); // the same as abovereturn 0;
}
Re[3]: Есть ли метод у класса - HasMethod - старая тема
Здравствуйте, Molchalnik, Вы писали:
M>Буст тяжелая библиотека, её не имеет смысла тащить из-за одной фичи, только если есть рассчёт на её плотное использование.
никогда не понимал этого... что значит тащить? вы исходники на дискете распростанняете?
большую часть моей карьеры boost используется везде. даже на своей домашней машине я устанавливаю boost сразу после компилятора...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: Есть ли метод у класса - HasMethod - старая тема
Здравствуйте, Molchalnik, Вы писали:
M>... Рано или поздно он либо получит тег вроде version 23 { }, в рамках которого зашлёт всё легаси лесом и устроит новые продуманные синтаксис и правила. Либо кто-то предоставит язык, с возможностями плюсов, но без legacy.
@here кстати, а разве такого никто до сих пор еще не сделал? никто не встречал ничего подобного?
думаю, на основе шланга сделать это было бы не очень сложно. (в смысле — сложно, но не настолько, как писать еще одного "убийцу плюсов")
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, Molchalnik, Вы писали:
M>Не нравится этот рубанок — стругай другим.
Сам-то рубанок как раз нравится. А вот ситуация, из-за которой в рубанке можно быстро и надежно закрепить лишь пару-тройку самых основных лезвий, а все остальные лезвия приходится приматывать проволокой и придерживать при работе, и производитель рубанка объявляет такой подход совершенно нормальным — не нравится категорически.
M>Плюсы на данный момент — универсальный язык, который может всё
Кто бы спорил.
M>но из-за попытки натянуть сову на глобус с поддержкой Legacy содержащий всё больше и больше костылей.
Поддержка legacy — совершенно отдельный вопрос. Я о том, что информацию о типах и возможности условной компиляции за пределами примитивных числовых выражений должно предоставлять ядро языка. Ситуация, в которой для этого необходимо создавать неестественные конструкции из возможностей, предназначенных совсем для другого, ненормальна по определению. И то, что люди, стоящие во главе разработки языка, явно ее поддерживают, отнюдь не делает им чести.
Re: Есть ли метод у класса - HasMethod - старая тема
Здравствуйте, Molchalnik, Вы писали:
M>Искал на нашем форуме SFINAE фишку по определению наличия класса в методе — нашёл только очень старую тему, написанную по старому стандарту, и которую крайне трудно адаптировать, потому что проще для каждого нового метода написать с нуля.
Ну и я свои пять копеек вставлю
В этом варианте проверяется возможность обращения к методу с заданным набором типов ФАКТИЧЕСКИХ параметров. Учитывается совместимость по const/volatile и по разновидностям ссылок — как параметров, так и самого объекта.
P> //static_assert(HasMember1<B>::value); // uncommment and get compilation error
P> //static_assert(HasMember1<int>::valuе); // the same as above
P>
Беда в том, что эти выражения и без static_assert приводят к ошибке. А хотелось бы, чтоб они давали false, так, чтоб к ним можно было применить логическое отрицание и использовать в SFINAE, например.
Здравствуйте, Molchalnik, Вы писали:
M>Искал на нашем форуме SFINAE фишку по определению наличия класса в методе — нашёл только очень старую тему, написанную по старому стандарту, и которую крайне трудно адаптировать, потому что проще для каждого нового метода написать с нуля.
M>Решил обновить вопрос. Не претендую на новизну и оригинальность, наверняка кто-то где-то уже сделал так или почти так. Но на нашем форуме не нашёл, поэтому счёл уместным поднять вопрос снова.
а компилятор то какой ? msvc умеет так
class Class {
public:
void Method();
};
int main() {
__if_exists(Class::Method) {
print("Class::Method exists");
}
__if_not_exists(Class::Method1) {
print("Class::Method1 not exists");
}
}
Re[2]: Есть ли метод у класса - HasMethod - старая тема
Здравствуйте, smbdnew, Вы писали:
S>msvc умеет так
Только при непосредственной квалификации имени метода именем класса. Проверить наличие метода у объекта, доступность виртуальной функции и т.п. уже не получится, хотя все это легко разрешимо на этапе компиляции.
Я ж говорю — есть только костыли, да и те кривые.
Re[3]: Есть ли метод у класса - HasMethod - старая тема
черновой набросок, далекий от совершенства, но дающий некоторое представление о возможных подходах.
Я выше уже писал, что возможность-то есть, но реализована она может быть лишь крайне извращенными (по отношению к основной парадигме языка). Да, окончательный синтаксис с макросом выглядит более-менее пристойно, но внутри него — откровенный ужас, разумного оправдания которому не существует. Это все от безысходности, которая активно поддерживается верхушкой разработчиков стандарта.
Re[3]: Есть ли метод у класса - HasMethod - старая тема
Здравствуйте, Molchalnik, Вы писали:
M>Буст тяжелая библиотека, её не имеет смысла тащить из-за одной фичи, только если есть рассчёт на её плотное использование.
Буст это не библиотека, а набор библиотек. Большинство очень даже легкие, не генерируют кода на неиспользуемые собственные фичи, и их можно использовать отдельно, не таща ничего лишнего. А для рабочего окружения разработчика буст и целиком не сказать чтобы тяжелый.
Вот этот подход, что буст "тяжелый" — он от непонимания, что это такое и как устроено.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.