How to find if a method of a particular prototype

2019-04-29 17:26发布

问题:

I'm working with some SFINAE features; currently in a portion of an application that must run in Linux and Windows; the compiler choices are MSVC (Visual Studio 2010 10.0) for Windows applications and GCC 4.4.5 for the Linux ones.

I must check if some given object provides some functions to perform a custom serialization and call this functions, or do a simple memcpy and sizeof(Object) while the custom serialization methods are not provided.

The problem is that a piece of code compile without warnings nor errors in MSVC but while compiling with GCC, the code is the following:

template
    <
        typename Type,
        typename Return,
        typename Parameter,
        Return (Type::*Pointer)(Parameter) const
    > struct sMemberMethodConst { };

template
    <
        typename Type,
        typename Return,
        typename Parameter,
        Return (Type::*)(Parameter)
    > struct sMemberMethod { };

template<typename T> struct sMemberMethodChecker
{
    template <typename Type> static char HasCustomSizeMethod(sMemberMethodConst<Type, size_t, void, &Type::Size> *);
    template <typename Type> static long HasCustomSizeMethod(...);
    template <typename Type> static char HasSerializeMethod(sMemberMethodConst<Type, size_t, void * const, &Type::Serialize> *);
    template <typename Type> static long HasSerializeMethod(...);
    template <typename Type> static char HasDeserializeMethod(sMemberMethod<Type, size_t, const void * const, &Type::Deserialize> *);
    template <typename Type> static long HasDeserializeMethod(...);
    // Other specific method checks...

    enum
    {
        HAS_CUSTOM_SIZE_METHOD =    (sizeof(HasCustomSizeMethod<T>(0)) == sizeof(char)),
        HAS_SERIALIZE_METHOD =      (sizeof(HasSerializeMethod<T>(0)) == sizeof(char)),
        HAS_DESERIALIZE_METHOD =    (sizeof(HasDeserializeMethod<T>(0)) == sizeof(char)),
        IS_CUSTOM =                 HAS_CUSTOM_SIZE_METHOD &&
                                    HAS_SERIALIZE_METHOD &&
                                    HAS_DESERIALIZE_METHOD,
        // Other 'shortcuts'...
    };

And the error that I'm getting while compiling with GCC is:

invalid parameter type 'void' in declaration template<class Type, class Return, class Parameter, Return (Type::* Pointer)(Parameter)const>

in the first line of the struct sMemberMethodChecker. I'm quite sure that I'm don't missing typenames nor misplacing words but I don't understand why I'm getting the error and doesn't understand the error.

I know that the MSVC is lax with standard while GCC conforms the standard pretty well so I'm wondering if the problem lies into the MSVC side that allows silly code!

Here are the questions:

  • Why am I getting the invalid parameter type 'void' error in the struct sMemberMethodChecker?.
  • Why the code is valid in MSVC but invalid in GCC?.
  • Is this code non-standard?.
  • Is the SFINAE trickery exclusive of C++11?

回答1:

Why I'm getting the invalid parameter type 'void' error in the struct sMemberMethodChecker?.

Why the code is valid in MSVC but it isn't in GCC?.

I believe that MSVC is being helpful however GCC is being stringent in this particular code. As it's somehow not allowing Return (Type::*)(void). However one need to dig it more to know the exact reason.

Is this code no-standard?.

Can't say until it doesn't compile. And searching standard for the features like SFINAE is not everyone's cup of tea.

Is the SFINAE trickery exclusive of C++11?

Not at all. SFINAE existed before C++11.
Here is the simplified way of what you want to do:

template<typename ClassName, typename ClassMethodType>
struct HasMethod
{
  template<typename Type, Type Object> struct Contains;
  typedef char (&yes)[2];

  template<typename Class, typename MethodType>
  static yes Check (Contains<MethodType, &Class::size>*);
  template<typename Class, typename MethodType>
  static char Check (...);

  static const bool value = (sizeof(Check<ClassName,ClassMethodType>(0)) == sizeof(char));
};

HasMethod<ClassName, ClassMethodType>::value gives you the answer if a certain type member method exists inside it or not.
As of now HasMethod<> is exclusive to the method naming size with user provided type. But you can create a macro for above code and make the function name configurable.

Here is a working demo with g++.