EDIT: I've updated the answer to allow for the given method to take arbitrary arguments, not simply have a void
signature
I think this will do the trick:
namespace details {
template<typename...>
struct voider
{
using type=void;
};
template<typename T>
struct GetReturnType;
// non member function
template<typename Ret, typename... Args>
struct GetReturnType<Ret(*)(Args...)>
{
using type = Ret;
};
// mmeber function
template<typename Ret, typename C, typename... Args>
struct GetReturnType<Ret(C::*)(Args...)>
{
using type = Ret;
};
}
template<typename...Ts>
using void_t = typename details::voider<Ts...>::type;
template<typename T, typename=void>
struct has_arithmetic_method : std::false_type{};
template<typename T>
struct has_arithmetic_method<T, void_t<decltype(&T::some_method)>> : std::is_arithmetic<typename details::GetReturnType<decltype(&T::some_method)>::type>::type{};
I'm using the voider concept to make sure that accessing some_method
on T
is well-formed, and then deriving from std::is_arithmetic
if it is. I had to add a helper to pull off the return type of an arbitrary function.
Some structs to test against. Notice how the ones that should pass take in different arguments for some_method
:
struct FailSomeMethod // lacks a some_method altogether
{
void some_other_method()
{
std::cout << "FailSomeMethod::some_other_method" << std::endl;
}
};
struct PassSomeMethod // has a some_method, but it's not arithmetic
{
void some_method()
{
std::cout << "PassSomeMethod::some_method" << std::endl;
}
};
struct PassArithmeticMethod
{
int some_method(int _a)
{
std::cout << "PassArithmeticMethod::some_method" << std::endl;
return 1;
}
};
struct PassArithmeticMethod2
{
double some_method()
{
return 1.0;
}
};
struct PassArithmeticMethod3
{
float some_method(int a, double b, float c, char d, bool e)
{
return 1.;
}
};
The actual test:
int main()
{
//test the has_arithmetic_method concept
std::cout << "Struct PassArithmeticMethod: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod>::value << std::endl;
std::cout << "Struct PassSomeMethod: " << std::boolalpha << has_arithmetic_method<PassSomeMethod>::value << std::endl;
std::cout << "Struct FailSomeMethod: " << std::boolalpha << has_arithmetic_method<FailSomeMethod>::value << std::endl;
std::cout << "Struct PassArithmeticMethod2: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod2>::value << std::endl;
std::cout << "Struct PassArithmeticMethod3: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod3>::value << std::endl;
}
Outputs:
Struct PassArithmeticMethod: true
Struct PassSomeMethod: false
Struct FailSomeMethod: false
Struct PassArithmeticMethod2: true
Struct PassArithmeticMethod3: true
Live Demo
Honestly, despite the (misleading) length of my answer, I feel that this is the simplest. The voider concept and the GetReturnType
helper are general purpose structs that can be easily re-used. I don't use any constexpr
in my code; I leave all the work for getting true
and false
to inheritance tricks.