I'm trying to find a way to simply check if a method of a given name exists in a c++ class using c++11 features, but without(!) checking the signature.
I was unable to find anything without the signature check, so I tried to use Valentin Milea's solution from here and modify it (see below), but my understanding of c++ is not deep enough to really grasp what's going on there:
#include <type_traits>
template <class C>
class HasApproxEqualMethod
{
template <class T>
static std::true_type testSignature(bool (T::*)(const T&, double) const);
template <class T>
static decltype(testSignature(&T::approx_equal)) test(std::nullptr_t);
template <class T>
static std::false_type test(...);
public:
using type = decltype(test<C>(nullptr));
static const bool value = type::value;
};
class Base {
public:
virtual ~Base();
virtual bool approx_equal(const Base& other, double tolerance) const;
};
class Derived : public Base {
public:
// same interface as base class
bool approx_equal(const Base& other, double tolerance) const;
};
class Other {};
static_assert(HasApproxEqualMethod<Base>().value == true, "fail Base");
static_assert(HasApproxEqualMethod<Other>().value == false, "fail Other");
// this one fails:
static_assert(HasApproxEqualMethod<Derived>().value == true, "fail Derived");
I think the root of the problem is that my approx_equal
uses base class references also in the derived class(es) and that doesn't match the signature anymore.
In the end, I want to construct a template comparison function that calls approx_equal
if it exists, or something else( e.g. ==
for strings etc., or fabs(a-b) <= tolerance
for floats and doubles). The approx_equal functions will then in turn call the template comparison for each member.
I actually got that part to work with an ugly workaround, where each class with an approx_equal
method also gets a member variable const static char hasApproxEqualMethod
. Then I check if that variable exists as suggested here, but that's certainly not the way to go.
What about using std::is_member_function_pointer_v
(requires c++17):
// Works
static_assert(std::is_member_function_pointer_v<decltype(&Base::approx_equal)>);
// Works
static_assert(std::is_member_function_pointer_v<decltype(&Derived::approx_equal)>);
// Fails as expected
static_assert(!std::is_member_function_pointer_v<decltype(&Other::approx_equal)>);
You can shorten it like this:
template<typename Class>
constexpr bool has_approx_equal()
{
return std::is_member_function_pointer_v<decltype(&Class::approx_equal)>;
}
static_assert(has_approx_equal<Base>());
static_assert(has_approx_equal<Derived>());
static_assert(!has_approx_equal<Other>());
Final solution uses SFINAE so that the compilation doesn't abort when tring to evaluate &Other::approx_equal
:
template<typename Class, typename Enabled = void>
struct has_approx_equal_s
{
static constexpr bool value = false;
};
template<typename Class>
struct has_approx_equal_s
<
Class,
std::enable_if_t
<
std::is_member_function_pointer_v<decltype(&Class::approx_equal)>
>
>
{
static constexpr bool value = std::is_member_function_pointer_v<decltype(&Class::approx_equal)>;
};
template<typename Class>
constexpr bool has_approx_equal()
{
return has_approx_equal_s<Class>::value;
};
static_assert(has_approx_equal<Base>());
static_assert(has_approx_equal<Derived>());
static_assert(has_approx_equal<Other>(), "Other doesn't have approx_equal.");
SFINAE makes sure it is possible to get false
value before trying to evaluate the static assertion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With