Boost logo

Boost :

From: Daniel Frey (d.frey_at_[hidden])
Date: 2003-02-03 17:49:29


I noticed that is_class actually detects classes only for the Metrowerks
and the VC++, other compilers use a fall-back implementation which works
by detecting everything but classes and thus everything which remains has
to be a class. This is IMHO broken as i.e. member functions are detected
as classes.

I have an implementation which works for both the GCC (3.0+ I think)
and the Intel 6/7 compiler (thus probably for most comparable EDG-based
compilers), I wonder if there is any interest in it and if my
implementation and the current one can be combined (or if my version works
for the MW and the VC, too). Here is the non-boostified code which can
IMHO be easily adapted (I tried to copy all relevant parts from my header,
I hope it's complete enough to understand it):

typedef char (&yes_t)[ 1 ];
typedef char (&no_t)[ 2 ];

// is_function
template< typename T > struct is_function
{
private:
   struct detail
   {
      static yes_t test( ... );
      static no_t test( const volatile void* );
   };

public:
   enum {
      value = sizeof detail::test( static_cast< T* >( 0 ) ) == sizeof( yes_t )
   };
};
template< typename T > struct is_function< T& > { enum { value = false }; };

// 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
{
private:
   template< bool, typename U > struct select { typedef U type; };
   template< typename U > struct select< true, volatile U > { typedef U type; };
   template< typename U > struct select< true, U& > { typedef U& type; };

public:
   typedef typename select< !is_function< T >::value, T >::type type;
};

// remove_cv
template< typename T > struct remove_cv
{
   typedef typename remove_const< typename remove_volatile< T >::type >::type type;
};

// Based on Paul Mensonides' technique found at
// http://groups.google.com/groups?selm=000001c1cc83%24e154d5e0%247772e50c%40c161550a

// detail::is_class::test
namespace detail
{
   namespace is_class
   {
      template< typename > mfl::no_t test( ... );
      template< typename T > mfl::yes_t test( void (T::*)() );
   }
}

// is_class (note that a union is detected as a class,
// I know of no way to separate unions from non-unions)
template< typename T > struct is_class
{
private:
   template< bool, typename > struct test { enum { value = false }; };
   template< typename U > struct test< true, U >
   {
      enum { value = sizeof detail::is_class::test< U >( 0 ) == sizeof( yes_t ) };
   };

public:
   enum { value = test< !is_function< T >::value, typename remove_cv< T >::type >::value };
};

This not only leads to safer class detection, it also allows us to
implement is_member_function without providing versions for every number
of parameters. Omitting some of the intermediate type-traits, here's how
it may look:

template< typename T > struct is_member_function_pointer
{
private:
   template< bool, typename > struct test { enum { value = false }; };
   template< typename U > struct test< true, U >
   {
      enum { value = !is_class< U >::value && !is_enum< U >::value && !is_member_variable_pointer< U >::value };
   };

public:
   enum { value = test<
             !is_fundamental< T >::value &&
             !is_reference< T >::value &&
             !is_pointer< T >::value &&
             !is_array< T >::value &&
             !is_function< T >::value, T >::value };
};
template< class C, typename T > struct is_member_function_pointer< T C::* > { enum { value = is_function< T >::value }; };
template< class C, typename T > struct is_member_function_pointer< T C::* const > { enum { value = is_function< T >::value }; };
template< class C, typename T > struct is_member_function_pointer< T C::* volatile > { enum { value = is_function< T >::value }; };
template< class C, typename T > struct is_member_function_pointer< T C::* const volatile > { enum { value = is_function< T >::value }; };

This may look overly complex, but this is due to the fact that the GCC
and the Intel compiler disagree about certain type deductions. The above
version combines both behaviours without the need for #ifdef. There are
probably some other things, but is_class is IMHO an important one, thus I
would like to see if there is interest in fixing it. Comments?

Regards, Daniel


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk