2017-01-30 3 views
5

Попытка создать способ определить, имеет ли данный класс заданную функцию, которая может быть вызвана, и возвращает некоторый тип.Характеристики типа, чтобы проверить, имеет ли класс функцию-член

Любая идея о том, что я делаю неправильно здесь? Есть ли лучший способ определить, может ли данный метод быть вызван с учетом класса?

#include <string> 
#include <type_traits> 

#define GENERATE_HAS_MEMBER_FUNC(func, rettype)        \ 
template<typename T, class Enable = void> struct has_##func;     \ 
template<typename T, class U> struct has_##func : std::false_type {};   \ 
template<typename T>               \ 
struct has_##func<T,               \ 
        typename std::enable_if<std::is_same<      \ 
         typename std::result_of<decltype (&T::func)(T)>::type, \ 
         rettype>::value>::type> : std::true_type{};    \ 
template<class T> constexpr bool has_##func##_v = has_##func<T>::value; 

GENERATE_HAS_MEMBER_FUNC(str, std::string) 
GENERATE_HAS_MEMBER_FUNC(str2, std::string) 
GENERATE_HAS_MEMBER_FUNC(funca, std::string) 
GENERATE_HAS_MEMBER_FUNC(strK, std::string) 
GENERATE_HAS_MEMBER_FUNC(fancy, std::string) 
GENERATE_HAS_MEMBER_FUNC(really, std::string) 

struct A1 { 
    virtual std::string str() const { return ""; } 
    std::string strK() const { return ""; } 
    virtual std::string fancy()=0; 
}; 

struct A2 : A1 { 
    std::string str() const override { return ""; } 
    std::string funca(); 
    std::string fancy() override { return ""; } 
    std::string really(int a=0) const { return std::to_string(a); } 

}; 

int main() { 
    static_assert(has_str_v<A1>, 
     "A1::str is virtual method with impl on base"); // MSVC: NO, clang: OK, GCC: NO 
    static_assert(has_strK_v<A1>, 
     "A1::strK is implemented inline "); // MSVC: NO, clang: OK, GCC: NO 
    static_assert(has_fancy_v<A1>, 
     "A1::fancy is a pure virtual method on base"); // MSVC: NO, clang: OK, GCC: NO 
    static_assert(!has_really_v<A1>, 
     "A1::really doesn't exist in A1"); // MSVC: OK, clang: OK, GCC: OK 

    static_assert(has_str_v<A2>, 
     "A2::str is override method "); // MSVC: OK, clang: OK, GCC: OK 
    static_assert(!has_str2_v<A2>, 
     "A2::str2 does not exist in A2"); // MSVC: NO, clang: OK, GCC: OK 
    static_assert(has_funca_v<A2>, 
     "A2::funca is defined (no impl) in A2"); // MSVC: OK, clang: OK, GCC: OK 
    static_assert(has_strK_v<A2>, 
     "A2::strK is implemented method on base"); // MSVC: OK, clang: OK, GCC: OK 
    static_assert(has_fancy_v<A2>, 
     "A1::fancy is a override of pure virtual method of base"); // MSVC: OK, clang: OK, GCC: OK 
    static_assert(has_really_v<A2>, 
     "A2::really has default param (can be invoked without params)"); // MSVC: OK, clang: NO, GCC: NO 
    return 0; 
} 

Некоторые неожиданности на этой реализации.

EDIT: При попытке реализовать @ Jarod42 и @Vittorio Romeo удивительные предложения:

#define GENERATE_HAS_MEMBER_FUNC(func, rettype)        \ 
template<class T> using _has_##func_chk =          \ 
     decltype(std::declval<T &>().func());         \ 
template<class T> constexpr bool has_##func##_v =        \ 
     is_detected_exact_v<rettype, _has_##func_chk, T>; 

теперь два случая испытания все еще не на VS2015 (не имеет никакого смысла!): static_assert (has_really_v, «A1 :: действительно не существует в A1»); static_assert (! Has_str2_v, "A2 :: str2 не существует в A2");

Возможно, что-то глупое, что мне не хватает ... никаких подсказок?

+1

Вы смотрите на 'std :: is_detected' (C++ 17, но реализуемый в C++ 11)? – Jarod42

ответ

9

Есть ли лучший способ определить, может ли данный метод быть вызван с учетом класса?

Да, вы можете использовать detection idiom, которые могут быть реализованы в C++ 11 (связанной страницы содержит действительную реализацию).

Пример: Cat У вас есть метод float Cat::purr(int)?

struct Cat { float purr(int){} }; 

template<class T> 
using has_purr = 
    decltype(std::declval<T&>().purr(std::declval<int>())); 

static_assert(std::experimental::is_detected_exact_v<float, has_purr, Cat>); 

wandbox example


Требуемая идиомы обнаружения С ++ 17 зависимостей тривиально реализовать в C++ 11:

template<class...> 
using void_t = void; 

struct nonesuch { 
    nonesuch() = delete; 
    ~nonesuch() = delete; 
    nonesuch(nonesuch const&) = delete; 
    void operator=(nonesuch const&) = delete; 
}; 

Вот fully C++11-compliant minimal example on wandbox.

+0

@ AndyG: обновил мой ответ. –

+0

Большое спасибо. Я пытаюсь реализовать в MSVC и запускать те же тесты. Но, сделав небольшие изменения, чтобы быстро протестировать сценарии в вашем коде Wandbox, похоже, что это самый лучший подход, – nCoder

+0

Значительно более последовательные результаты для компиляторов, но парочка тестов терпит неудачу ... как глупо моя ошибка? (не вижу этого): P – nCoder

Смежные вопросы