rubenvb
rubenvb

Reputation: 76795

is_member_function_pointer implementation

I am trying to implement my own is_member_function_pointer and I'm having trouble with it.

namespace __implementation
{
    // integral_constant
    template<typename T, T v>
    struct integral_constant
    {
        static constexpr T result = v;
        typedef integral_constant<T,v> type;
        constexpr operator T() { return result; }
    };
    // true_type
    typedef integral_constant<bool, true> true_type;
    // false_type
    typedef integral_constant<bool, false> false_type;
    // remove_const
    template<typename T> struct remove_const { typedef T type; };
    template<typename T> struct remove_const<const T> { typedef T type; };
    // remove_volatile
    template<typename T> struct remove_volatile { typedef T type; };
    template<typename T> struct remove_volatile<volatile T> { typedef T type; };
    // remove_cv
    template<typename T> struct remove_cv
    { typedef typename remove_volatile<typename remove_const<T>::type>::type type; };
    // is_function - ugliness ahead due to variadic functions!
    template<typename T, typename... Args> struct is_function : public false_type {};
    template<typename T, typename... Args> struct is_function<T(Args...)> : public true_type {}; // normal function
    template<typename T, typename... Args> struct is_function<T(Args......)> : public true_type {}; // variadic function
    // is_member_function_pointer
    template<typename T> struct is_member_function_pointer : public false_type {};
    template<typename T, typename Class> struct is_member_function_pointer<T Class::*> : public is_function<T> {};   
}
/*
 * Short forms: either alias templates or constexpr functions
 */
// remove_const
template<typename T>
using remove_const = typename __implementation::remove_const<T>::type;
// remove_volatile
template<typename T>
using remove_volatile = typename __implementation::remove_volatile<T>::type;
// remove_cv
template<typename T>
using remove_cv = typename __implementation::remove_cv<T>::type;
// is_member_function_pointer
template<typename T>
constexpr bool is_member_function_pointer { return __implementation::is_member_function_pointer<T>::result; }
// is_function
template<typename T>
constexpr bool is_function() { return __implementation::is_function<typename __implementation::remove_cv<T>::type>::result; }

The problem lies in normal function pointers, which fail to work as they should for the member_function_pointer template. Also, const member function pointers aren't recognized as member_function_pointer, which is unfortunate. How can I fix this implementation? I believe I can enable_if a regular function pointer specialization to work around the normal function pointer problem, but I can't see a way out of the const member function pointer problem (I've literally tried adding remove_cv's and const's everywhere in the type trait definition, to no avail). I can see the current is_member_function_pointer doesn't handle a const class's member function, but I don't know what syntactical magic I can use to do so.

Any help is appreciated.

Upvotes: 2

Views: 818

Answers (2)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507403

Alternatively

template<typename T> struct remove_c { typedef T type; };
template<typename T> struct remove_c<T const> { typedef T type; };

template<bool C> 
struct bool_ { static bool const value = C; };

template<typename T, typename U>
struct is_same : bool_<false> {};

template<typename T>
struct is_same<T, T> : bool_<true> { };

template<typename T, typename = bool_<true> >
struct is_function : bool_<false> {};

struct C { };

template<typename T>
struct is_function< T, 
   bool_<is_same<typename remove_c<T>::type const C::*, 
                 typename remove_c<T>::type       C::*>::value> > 
  : bool_<true> {};

template<typename T>
struct is_member_function_pointer_impl : bool_<false> {};

template<typename T, typename C>
struct is_member_function_pointer_impl<T C::* const volatile>
  : is_function<T> {};

template<typename T>
struct is_member_function_pointer 
  : is_member_function_pointer_impl<T const volatile> {};

Upvotes: 2

rubenvb
rubenvb

Reputation: 76795

A very short talk on Chat has brought this easy solution:

// is_member_function_pointer
template<typename T> struct is_member_function_pointer : public false_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...)> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) const> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) volatile> : public true_type {};  
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) const volatile> : public true_type {};

Which includes specializations for volatile as well.

EDIT as @Xeo and @Johannes pointed out, I missed the ref-qualifier (aka rvalue for *this) versions:

template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) &> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) const &> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) volatile &> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) const volatile &> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) &&> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) const &&> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) volatile &&> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...) const volatile &&> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...)> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) const> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) volatile> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) const volatile> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) &> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) const &> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) volatile &> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) const volatile &> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) &&> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) const &&> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) volatile &&> : public true_type {};
template<typename T, typename Class, typename... Args> struct is_member_function_pointer<T (Class::*)(Args...,...) const volatile &&> : public true_type {};

Which is still quite doable. It's clearer then crazy template magic and numerous helper templates IMHO.

EDIT2 to clarify, the above are all in an implementation namespace and wrapped around by

template<typename T> constexpr bool
is_member_function_pointer() { return __implementation::is_member_function_pointer<remove_cv<T>>::result; }

Where remove_cv is alias template'd to the convenient

template<typenamen T> using
remove_cv = typename __implementation::remove_cv<T>::type;

I suppose there are other, and maybe better ways, but this one is at least clear to the reader without any further SFINAE tricks like applied in libc++ or libstdc++, IMHO.

Upvotes: 4

Related Questions