The code below
- handles member function constness correctly
- is agnostic of the functions return types
- prints a comprehensive error on failure
It could be even shorter with C++14, where you don't have to specify the return types of implemented functions and have templated variable declarations. If you want to handle rvalue overloads correctly, you need to provide another overload to as_memfun
.
If testing for member functions alone is not enough, there is another approach in the last section, which offers far better customization options but is also lengthier to setup.
#include <utility>
#include <functional>
namespace detail {
template<typename T> struct _false : std::integral_constant<bool, false> { };
template<typename T> struct HasNone {
static_assert(_false<T>::value, "No valid method found");
};
template<typename T, typename R>
constexpr auto as_memfun (R (T::* arg) ())
-> R (T::*) ()
{ return arg; }
template<typename T, typename R>
constexpr auto as_memfun (R (T::* arg) () const)
-> R (T::*) () const
{ return arg; }
template<typename T> constexpr auto check_has_two(int)
-> decltype(as_memfun(&T::two))
{ return as_memfun(&T::two); }
template<typename T> constexpr auto check_has_two(...)
-> HasNone<T>;
template<typename T> constexpr auto check_has_one(int)
-> decltype(as_memfun(&T::one))
{ return as_memfun(&T::one); }
template<typename T> constexpr auto check_has_one(...)
-> decltype(check_has_two<T>(0))
{ return check_has_two<T>(0); }
template<typename T>
struct res { constexpr static auto detail = check_has_one<T>(0); };
}
template<typename T>
auto func(T t) -> decltype((t.*detail::res<T>::detail)()) {
return (t.*detail::res<T>::detail)();
}
And here are some test you would probably like to have
struct One {
void one();
};
struct Two {
void two();
};
struct TestBoth {
char one() const;
void two();
};
struct TestWilderStuff {
int one;
void two() const;
};
int main() {
func(One{});
func(Two{});
func(TestBoth{});
static_assert(decltype(func(TestBoth{})){} == 0, "Failed function selection");
func(TestWilderStuff{});
}
Since you seem to have more extensive constructions in mind than just testing for member function existence, here is the beginning of a vastly more powerful mechanism. You can use it as a drop-in replacement for the above solution and although it is far lengthier, it offers more customization and the possibility to do elaborate tests on your types in every step of the way.
#include <utility>
#include <functional>
namespace detail {
template<typename T> struct _false :
std::integral_constant<bool, false> { };
template<typename T> struct HasNone {
static_assert(_false<T>::value, "No valid method found");
};
// Generic meta templates used below
namespace Generics {
template<typename Getter, typename Else>
struct ChainGetter {
template<typename T> constexpr static auto get_(int)
-> decltype(Getter::template get<T>())
{ return Getter::template get<T>(); }
template<typename T> constexpr static auto get_(...)
-> decltype(Else::template get<T>())
{ return Else::template get<T>(); }
template<typename T> constexpr static auto get()
-> decltype(get_<T>(0))
{ return get_<T>(0); }
};
template<typename Getter, typename Test>
struct TestGetter {
template<typename T, typename R> using _type = R;
template<typename T> constexpr static auto get_()
-> decltype(Getter::template get<T>())
{ return Getter::template get<T>(); }
template<typename T> constexpr static auto test()
-> decltype(Test::template test<T>(get_<T>()));
template<typename T> constexpr static auto get()
-> _type<decltype(test<T>()),
decltype(get_<T>())
>
{ return get_<T>(); }
};
template<template<typename> class F>
struct FailGetter {
template<typename T>
constexpr static auto get() -> F<T>;
};
}
// Test only exists for member function pointer arguments
struct IsMemberFunctionTest {
template<typename _, typename T, typename R>
constexpr static void test (R (T::* arg) ());
template<typename _, typename T, typename R>
constexpr static void test (R (T::* arg) () const);
};
// Get member pointer to T::one
struct GetOne {
template<typename T>
constexpr static auto get() -> decltype(&T::one) { return &T::one; }
};
// Get member pointer to T::two
struct GetTwo {
template<typename T>
constexpr static auto get() -> decltype(&T::two) { return &T::two; }
};
using namespace Generics;
using getter_fail = FailGetter<HasNone>;
using get_two_tested = TestGetter<GetTwo, IsMemberFunctionTest>;
using getter_two = ChainGetter<get_two_tested, getter_fail>;
using get_one_tested = TestGetter<GetOne, IsMemberFunctionTest>;
using getter_one = ChainGetter<get_one_tested, getter_two>;
template<typename T>
struct result { constexpr static auto value = getter_one::template get<T>(); };
}
template<typename T>
auto func(T t) -> decltype((t.*detail::result<T>::value)()) {
return (t.*detail::result<T>::value)();
}