Check If a Class Has a Member Function of a Given Signature

Check if a class has a member function of a given signature

I'm not sure if I understand you correctly, but you may exploit SFINAE to detect function presence at compile-time. Example from my code (tests if class has member function size_t used_memory() const).

template<typename T>
struct HasUsedMemoryMethod
{
template<typename U, size_t (U::*)() const> struct SFINAE {};
template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
template<typename U> static int Test(...);
static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};

template<typename TMap>
void ReportMemUsage(const TMap& m, std::true_type)
{
// We may call used_memory() on m here.
}
template<typename TMap>
void ReportMemUsage(const TMap&, std::false_type)
{
}
template<typename TMap>
void ReportMemUsage(const TMap& m)
{
ReportMemUsage(m,
std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
}

Check if a class has a method with a given name but any signature

The problem in your code is that you check that a class has method that accept, as first argument a constant reference to an object of the same class

template <class T>
static std::true_type testSignature(bool (T::*)(const T&, double) const);
// .......................................^...........^ same class

but inside Derived you define a method that receive an object of a different class (Base)

// ...VVVVVVV  object is Derived
class Derived : public Base {
public:
// same interface as base class
bool approx_equal(const Base& other, double tolerance) const;
// .....................^^^^ method accept Base
};

A possible solution is relax the test in HasApproxEqualMethod to accept also objects of different classes

template <class T, class U>
static std::true_type testSignature(bool (T::*)(const U&, double) const);
// now class and argument are different...^...........^

This way is satisfied also

static_assert(HasApproxEqualMethod<Derived>().value == true, "fail Derived");

If you want avoid at all the signature check, you can try something similar

template <typename T>
constexpr auto haemHelper (T const &, int)
-> decltype( &T::approx_equal, std::true_type{} );

template <typename T>
constexpr std::false_type haemHelper (T const &, long);

template <typename T>
using HasApproxEqualMethod = decltype( haemHelper(std::declval<T>(), 0) );

but, this way, HasApproxEqualMethod<T> is std::true_type also when T has a approx_equal method with a completely different signature or also when approx_equal is a simple member (a variable).

Templated check for the existence of a class member function?

Yes, with SFINAE you can check if a given class does provide a certain method. Here's the working code:

#include <iostream>

struct Hello
{
int helloworld() { return 0; }
};

struct Generic {};

// SFINAE test
template <typename T>
class has_helloworld
{
typedef char one;
struct two { char x[2]; };

template <typename C> static one test( decltype(&C::helloworld) ) ;
template <typename C> static two test(...);

public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
std::cout << has_helloworld<Hello>::value << std::endl;
std::cout << has_helloworld<Generic>::value << std::endl;
return 0;
}

I've just tested it with Linux and gcc 4.1/4.3. I don't know if it's portable to other platforms running different compilers.

Check if class has function with signature

The best way I know of is checking if you can actually call the function and if it returns the type you expect. Here's an example of how to detect if a class C has a receive method which takes const Event& as a parameter and "returns" void. The detection does not care whether the method is implemented in the class C directly or in some base class that C derives from, neither does it care whether there are further defaulted parameters. Adapt as needed.

template< typename C, typename Event, typename = void >
struct has_receive
: std::false_type
{};

template< typename C, typename Event >
struct has_receive< C, Event, typename std::enable_if<
std::is_same<
decltype( std::declval<C>().receive( std::declval<const Event&>() ) ),
void
>::value
>::type >
: std::true_type
{};

Is `void_t` necessary to check if a class has a method with a specific signature?

This question explains in detail how void_t (otherwise known as the detection idiom) works. The key point is that the specialisation will only be considered if the type of the second template parameter evaluates to void.

In this case, it just so happens that your print() method returns void, so decltype(declval<T>().print()) is also void. But if your print() returned something else, say bool, then the specialisation would not match and would not be used.

Detect if class has constructor with signature

How can I extend this to the check I want to perform?

You could use decltype(...) on an T{...} expression as follows:

struct Foo 
{
Foo(Archive&) { }
};

struct Bar
{
// unsupported
};

template<class T>
using HasUnarchiveConstructorImpl =
decltype(T{std::declval<Archive&>()});

template <class T>
using HasUnarchiveConstructor =
std::experimental::is_detected<HasUnarchiveConstructorImpl, T>;

static_assert(HasUnarchiveConstructor<Foo>::value);
static_assert(!HasUnarchiveConstructor<Bar>::value);

live example on wandbox


Or how can I do it in a different way?

See Howard Hinnant's answer.

Checking function with given signature then compile differently

As far as I can tell, we need class template specialization here. Not even C++20 requires-clauses can be applied to virtual functions, so the only thing we can do is have the whole class change.

template<typename T> // using C++20 right now to avoid SFINAE madness
struct Foo {
virtual ~Foo() = default;
virtual std::shared_ptr<T> do_something(int a) = 0;
};
template<std::constructible_from<int> T>
struct Foo<T> {
virtual ~Foo() = default; // to demonstrate the issue of having to duplicate the rest of the class
virtual std::shared_ptr<T> do_something(int a) {
return std::make_shared<T>(a);
}
};

If there is a lot of stuff in Foo, you can avoid duplicating it with a heap of upfront cost by moving do_something to its own class.

namespace detail { // this class should not be touched by users
template<typename T>
struct FooDoSomething {
virtual ~FooDoSomething() = default;
virtual std::shared_ptr<T> do_something(int a) = 0;
};
template<std::constructible_from<int> T>
struct FooDoSomething<T> {
virtual ~FooDoSomething() = default;
virtual std::shared_ptr<T> do_something(int a);
};
}
template<typename T>
struct Foo : detail::FooDoSomething<T> {
// other stuff, not duplicated
// just an example
virtual int foo(int a) = 0;
};
namespace detail {
template<std::constructible_from<int> T>
std::shared_ptr<T> FooDoSomething<T>::do_something(int a) {
Foo<T> &thiz = *static_cast<Foo<T>*>(this); // if you need "Foo things" in this default implementation, then FooDoSomething is *definitely* unsafe to expose to users!
return std::make_shared<T>(thiz.foo(a));
}
}

Godbolt

To convert this down from C++20, replace the concept-based specialization with old-type branching:

// e.g. for the simple solution
template<typename T, bool = std::is_constructible_v<T, int>>
struct Foo { // false case
// etc.
};
template<typename T>
struct Foo<T, true> { // true case
// etc.
};
// or do this to FooDoSomething if you choose to use that

A runtime error is much easier, at least in C++17 and up, since you can just use a if constexpr to avoid compiling the problematic code

template<typename T>
struct Foo {
virtual ~Foo() = default;
virtual std::shared_ptr<T> do_something(int a) {
if constexpr(std::is_constructible_v<T, int>) return std::make_shared<T>(a);
else throw std::logic_error("unimplemented Foo<T>::do_something");
}
};


Related Topics



Leave a reply



Submit