1

I am currently wiring an application that has several implementations for a single purpose. It is checked at runtime if the appropriate implementation can be used or if a fallback is used.

For that purpose I want all implementations to implement a static function static bool is_available().

As static functions cannot be made abstract in the underlying base class, is there some preprocessor magic that will allow me to output an error message if the method is not statically implemented in the child class?

10
  • why is is_available() static? Commented Feb 28, 2019 at 7:56
  • Are you trying to enforce (at compile time) that all subclasses of a base have a static member function is_available? Commented Feb 28, 2019 at 7:57
  • 2
    I think stackoverflow.com/questions/10957924/… should answer your question. Commented Feb 28, 2019 at 8:00
  • 2
    You might be able to use CRTP. All subclasses would have to inherit from CanCheckAvailablility<MyClass> (you'll think of a better name). The CRTP base would just call is_available in an unevaluated context and fail to compile if the function is not available. There might be better solutions. That's just the first that came to mind. Commented Feb 28, 2019 at 8:02
  • 1
    I think this is xyproblem.info Commented Feb 28, 2019 at 8:15

4 Answers 4

3

In the same vein that user9400869's answer, you can define a trait using SFINAE to check, at compilation time, the availability of your classes:

#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;

This implies C++17 and constexpr functions is_available for the libs:

#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';
}

Full demo


If C++17 is not an option, you can implement std::is_invocable_r using C++14 features only.

If constexpr static member functions are not an option for your library classes, you cannot rely on std::true_type and std::false_type and must use run-time result gathering:

#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(); }
};

Full demo

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you VERY MUCH for this detailed answer! Solves my problem perfectly!
1

you could test this at compiletime with templates. It goes something like this (sorry not tested):

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 {}

Then you can use somewhere in your code:

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

Where Implementation is the class you wish to test. Have a look at std::type_traits for examples of this.

Comments

0

I would suggest an alternative: tag dispatching:

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 "blocks" Inheritance, contrary to:

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()

Comments

0

If you just want to check for a known type you can use requires expressions (c++ 20):

template<typename ToCheck>
constexpr bool checkAvailable() {
    return requires() { { ToCheck::is_available() } -> std::same_as<bool>; };
}

then to use it:

static_assert(checkAvailable<Foo>() == true, "error");

If you want to learn more about requires expressions you can look here

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.