I found several questions & answers on SO dealing with detecting at compile time (via SFINAE) whether a given class has a member of certain name, type, or signature. However, I couldn't find one that also applies to static public member functions (when pointer-to-member tricks won't work). Any ideas?
Following may help: (https://ideone.com/nDlFUE)
#include <cstdint>
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \
template <typename U> \
class traitsName \
{ \
private: \
template<typename T, T> struct helper; \
template<typename T> \
static std::uint8_t check(helper<signature, &funcName>*); \
template<typename T> static std::uint16_t check(...); \
public: \
static \
constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
}
Then define a traits:
DEFINE_HAS_SIGNATURE(has_foo, T::foo, void (*)(void));
Here's one way:
#include <type_traits>
template<typename, typename>
struct has_fun;
template<typename T, typename Ret, typename... Args>
struct has_fun<T, Ret(Args...)> {
template<typename U, U> struct Check;
template<typename U>
static std::true_type Test(Check<Ret(*)(Args...), &U::fun>*);
template<typename U>
static std::false_type Test(...);
static const bool value = decltype(Test<T>(0))::value;
};
It's written for a function called fun
. Use it like has_fun<T, int(int, int)>::value
.
Here's another:
#include <type_traits>
template<typename, typename>
struct has_fun;
template<typename T, typename Ret, typename... Args>
struct has_fun<T, Ret(Args...)> {
struct No {}; // need a unique type for the second overload
// so it doesn't match Ret and give a false positive
template<typename U>
static auto Test(int) -> decltype( U::fun(std::declval<Args>()...) );
template<typename U>
static No Test(...);
static const bool value =
std::is_same<decltype(Test<U>(0)), Ret>{};
};
It might be sensible to test whether the return type of the function is convertible to Ret
instead of checking for exatch match. Use is_convertible
instead of is_same
in that case and, at the same time, check that return type is different than No
(as Yakk points out, there are types out there that can be constructed from just about anything).
&&
it does nothing? –
Seaver std::function<whatever>
is_convertible
from your has_fun<whatever>::No
. –
Seaver using R = decltype(Test<U>(0)); value = !is_same<R, No>{} && is_convertible<R, Ret>{};
should avoid that, right? –
Enlargement Test<U>(0))
should be Test<T>(0))
–
Ozonosphere Simply invoke the member function and discard the result in a SFINAE context. If it succeeds, the method exists. If it fails, the method does not.
// not needed in C++1y
template<class T,class V=void>using enable_if_t=typename enable_if<T,V>::type;
// If the other tests fail, the type T does not have a method `foo` of
// signature Sig. The class=void parameter is an implementation detail
// that in an industrial quality implementation we would hide in a helper
// template type.
template<class T,class Sig,class=void>struct has_foo:std::false_type{};
// For R(Args...), we attempt to invoke `T::foo` with (Args...), then check
// if we can assign the return value to a variable of type R.
template<class T,class R,class...Args>
struct has_foo<T,R(Args...),
enable_if_t< // std:: in C++1y
std::is_convertible<
decltype( T::foo( std::declval<Args>()... ) ),
R
>::value
&& !std::is_same<R, void>::value
>
>: std::true_type {};
// for `void` return value, we only care if the function can be invoked,
// no convertible test required:
template<class T,class...Args>
struct has_foo<T,void(Args...),
decltype( void(T::foo( std::declval<Args>()... ) ) )
>: std::true_type {};
use:
has_foo< bar, int(int) >::value
which checks if int r = T::foo( 7 )
is a valid expression, and not for exact signature match.
foo
's return type is void
(both partial specialization match) - I added a !std::is_same<R, void>::value
inside the std::enable_if_t
to correct this, but I was wondering if there was a better way? –
Berlyn void
is not convertible to void
last time I read the C++ standard definition of is_convertible
. In addition, R=void
should be less specialized than a hard-coded void
. Your fix should be good enough, I'm just curious what compiler needs it. (I thought is_convertible<void,void>
required regular void
, which is proposed for C++20). –
Seaver -std=c++14
). –
Berlyn void
is more specialized than R
) but not for the target function. –
Berlyn R
is not void
requirement in there. I guess claiming void is convertible to void is a common thing for compilers to say. –
Seaver Here is demo code (similar to another code snippet in here), except simplified and supplied with example code.
#include <iostream>
#define DEFINE_FUNCTION_CHECKER(traitsName, funcName, signature) \
template <typename U> class traitsName \
{ \
template<typename T, T> struct helper; \
template<typename T> static std::true_type check(helper<signature, T::funcName>*); \
template<typename T> static std::false_type check(...); \
public: \
static constexpr bool value = decltype(check<U>(0))::value; \
};
template <class T>
class Factory
{
public:
Factory()
{
std::cout << "Factory of " << typeid(T).name() << std::endl;
}
};
class A
{
public:
static void RegisterFactory()
{
static Factory<A> factory;
}
};
class B
{
public:
static Factory<B> factory;
};
DEFINE_FUNCTION_CHECKER(has_RegisterFactory, RegisterFactory, void (*)(void))
template <class T>
void func()
{
if constexpr (has_RegisterFactory<T>::value)
T::RegisterFactory();
}
int main()
{
func<A>();
func<B>();
}
Code will print Factory of class A
, even thus there exists static class factory for B
class as well, but it's unused (B's factory constructor never called).
© 2022 - 2024 — McMap. All rights reserved.
class foo { private: static bool bar(); };
do you want to say 'foo
has astatic
methodbar
' or not? – Seaver