Проверьте, доступна ли статическая функция в классе во время компиляции

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

Для этого я хочу, чтобы все реализации реализовали статическую функцию static bool is_available().

Поскольку статические функции нельзя сделать абстрактными в базовом классе, существует ли какая-то магия препроцессора, которая позволит мне выводить сообщение об ошибке, если метод не реализован статически в дочернем классе?


person Nidhoegger    schedule 28.02.2019    source источник
comment
почему is_available() статичен?   -  person Stephan Lechner    schedule 28.02.2019
comment
Вы пытаетесь обеспечить (во время компиляции), чтобы все подклассы базы имели статическую функцию-член is_available?   -  person Indiana Kernick    schedule 28.02.2019
comment
Я не хочу создавать ненужные классы. is_available будет просто проверять, можно ли загрузить библиотеки с помощью dlopen, поэтому пока нет необходимости создавать экземпляр этого класса для проверки доступности.   -  person Nidhoegger    schedule 28.02.2019
comment
@ Kerndog73 точно   -  person Nidhoegger    schedule 28.02.2019
comment
Я думаю, stackoverflow.com/questions/10957924/ должен ответить на ваш вопрос.   -  person Martijn Otto    schedule 28.02.2019
comment
Возможно, вы сможете использовать CRTP. Все подклассы должны быть унаследованы от CanCheckAvailablility<MyClass> (вы придумаете лучшее имя). База CRTP просто вызовет is_available в неоцененном контексте и не сможет скомпилировать, если функция недоступна. Могут быть лучшие решения. Это только первое, что пришло в голову.   -  person Indiana Kernick    schedule 28.02.2019
comment
вместо использования статической функции, как насчет использования виртуальных частных функций в базовом классе?   -  person Mayur    schedule 28.02.2019
comment
Не будет ли это принудительно выполнено во время компиляции кодом, вызывающим is_available? У меня есть решение проблемы, которого я не очень понимаю.   -  person Indiana Kernick    schedule 28.02.2019
comment
Я думаю, это xyproblem.info   -  person Mayur    schedule 28.02.2019
comment
Чтобы проверить, существует ли функция, вам нужно будет попытаться ее вызвать. Даже если вы используете базу CRTP, вам все равно нужно будет попытаться вызвать функцию на базе. Это может быть деструктор. Вы должны полностью квалифицировать функцию, чтобы проверить, существует ли она, поэтому любая проверка, которую вы выполняете, должна выполняться для каждого подкласса. Если вы пишете проверку для всех подклассов, вы можете просто реализовать is_available во всех подклассах! Если вам нужна автоматическая проверка, вам нужно будет использовать абстрактную виртуальную функцию. Компилятор обеспечит его реализацию.   -  person Indiana Kernick    schedule 28.02.2019


Ответы (3)


Точно так же, как user9400869 answer, вы можете определить черту, используя SFINAE, чтобы во время компиляции проверить доступность ваших классов:

#include <type_traits>

template<class LIB, class = void>
struct is_available
: std::false_type
{};

template<class LIB>
struct is_available<LIB, std::enable_if_t<std::is_invocable_r<bool, decltype(LIB::is_available)>::value>>
: std::integral_constant<bool, LIB::is_available()>
{};

template<class LIB>
constexpr bool is_available_v = is_available<LIB>::value;

Это подразумевает C ++ 17 и constexpr функции is_available для библиотек:

#include <iostream>

struct A {};
struct B { static constexpr bool is_available() { return false; } };
struct C { static constexpr bool is_available() { return true; } };

int main()
{
    std::cout << is_available_v<A> // 0
              << is_available_v<B> // 0
              << is_available_v<C> // 1 :)
              << '\n';
}

Полная демонстрация


Если C ++ 17 не подходит, вы можете реализовать std::is_invocable_r с помощью C Только функции ++ 14.

Если constexpr статические функции-члены не подходят для ваших библиотечных классов, вы не можете полагаться на std::true_type и std::false_type и должны использовать сбор результатов во время выполнения:

#include <type_traits>

template<class LIB, class = void>
struct is_available
{
    static bool value() { return false; }
};

template<class LIB>
struct is_available<LIB, std::enable_if_t<std::is_invocable_r<bool, decltype(LIB::is_available)>::value>>
{
    static bool value() { return LIB::is_available(); }
};

Полная демонстрация

person YSC    schedule 28.02.2019
comment
ОГРОМНОЕ спасибо за подробный ответ! Решает мою проблему отлично! - person Nidhoegger; 28.02.2019

вы можете проверить это во время компиляции с помощью шаблонов. Это выглядит примерно так (извините, не проверено):

template<class Type_to_test, class Technical_Detail = void>
struct has_isAvilable : std::false_type {}

template<class Type_to_test>
struct has_isAvilable<Type_to_test, std::enable_if_t<
  std::is_same<bool,decltype(Type_to_test::is_available())>::value
> > : std::true_type {}

Затем вы можете использовать где-нибудь в своем коде:

static_assert(has_isAvilable<Implementation>::value,"Usefull error message");

Где Реализация - это класс, который вы хотите протестировать. Взгляните на std::type_traits, чтобы увидеть примеры этого.

person Community    schedule 28.02.2019

Я бы предложил альтернативу: отправка тегов:

template <typename T> struct Tag {};

struct Base { /**/ };
struct Child1 : Base { /**/ };
struct Child2 : Base { /**/ };

bool is_available(Tag<Base>) {/*..*/}
bool is_available(Tag<Child1>) {/*..*/}
bool is_available(Tag<Child2>) {/*..*/}

Tag "блокирует" наследование, в отличие от:

struct Base { static constexpr bool is_available() { return false; } };
struct Child1 : Base { static constexpr bool is_available() { return true; } };
struct Child2 : Base { /**/ };

static_assert(Base::is_available() == false);
static_assert(Child1::is_available() == true);
static_assert(Child2::is_available() == false); // Call Base::is_available()
person Jarod42    schedule 28.02.2019