You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@stdcxx.apache.org by Scott Zhong <sc...@roguewave.com> on 2006/12/23 15:58:45 UTC

type_traits is_integral implementation

I try to implement type_traits using partial specialization as follows:

template <class T> struct __remove_s { typedef T type; };
template <class T> struct __remove_s<signed T> { typedef T type; };
template <class T> struct __remove_s<unsigned T> { typedef T type; };
template <class T> struct __remove_s<short T> { typedef T type; };
template <class T> struct __remove_s<long T> { typedef T type; };

template <class T> struct is_integral:
	__is_same<typename __remove_s<typename
__remove_cv<T>::type>::type, int> { };

But signed is actually a type and T is already a type, and signed int is
a compound type.  When compiling the above code in MSVC 8.0, it would
give an error that states "too many template arguments".  I went ahead
and read the book you had pointed to me, the implementation in there is
using 3 enum? with a 3rd struct containing index_of template that would
search though the enum with return value of greater than 0 if found, 0
if not found.  I decided to implement it a different way as follows:

template <typename T> struct __unsigned_ints { typedef T type; };
template <> struct __unsigned_ints<unsigned int> { typedef int type; };
template <> struct __unsigned_ints<unsigned char> { typedef int type; };
template <> struct __unsigned_ints<unsigned short int> { typedef int
type; };
template <> struct __unsigned_ints<unsigned long int> { typedef int
type; };

template <typename T> struct __signed_ints {typedef T type; };
template <> struct __signed_ints<long int> { typedef int type; };
template <> struct __signed_ints<int> { typedef int type; };
template <> struct __signed_ints<signed char> { typedef int type; };
template <> struct __signed_ints<short int> { typedef int type; };

template <typename T> struct __other_ints {typedef T type; };
template <> struct __other_ints<bool> { typedef int type; };
template <> struct __other_ints<char> { typedef int type; };
#ifndef _RWSTD_NO_WCHAR_T
template <> struct __other_ints<wchar_t> { typedef int type; };
#endif

template <class T> struct is_integral:
	__is_same<typename __other_ints<typename
__unsigned_ints<typename __signed_ints<typename
__remove_cv<T>::type>::type>::type>::type, int> { };

Not really sure the internal workings of a template, does it treat each
specialization as an enum? My implementation does generate 5 template
instantiation whenever is_integral is called, would that adversely
affect performance? Please let me know if there is a better
implementation.

Thank you,   

Yu (Scott) Zhong
Consulting Engineer
Rogue Wave Software, a Quovadxtm division
scottz@roguewave.com
ph: 303 545 3182
 

Re: type_traits is_integral implementation

Posted by Martin Sebor <se...@roguewave.com>.
Martin Sebor wrote:
> Scott Zhong wrote:
> 
>> I try to implement type_traits using partial specialization as follows:
[...]
 > The goal should be to come up with an elegant,
> compact implementation that's fast to compile and that generates
> the least number of specializations of implementation classes
> (deep template recursion is the enemy of compile time efficiency).
[...]
> // __is_std_signed specializations for each standard signed
> // integer type (T must not be cv-qualified)
> template <> struct __is_std_signed<signed char>: true_type { };

FWIW, changing this and the rest of the __is_std_xxx helpers to

   template <>
   struct __is_std_signed<const volatile signed char>
       : true_type { };

and the is_signed, is_unsigned, and is_integral traits to avoid
using remove_cv by adding const volatile as shown below reduced
gcc 4.1 compilation time of the test program by 10%.

[...]
> template <class T>
> struct is_signed:
>   integral_constant<bool,
>                        __is_std_signed<typename remove_cv<T>::type>::value
>                     || __is_ext_signed<typename remove_cv<T>::type>::value>
> { };


     template <class T>
     struct is_signed
         : integral_constant<bool,
                  __is_std_signed<const volatile T>::value
               || __is_ext_signed<const volatile T>::value> >
     { };

and

> template <class T>
> struct is_integral:
>   integral_constant<
>     bool,
>        is_signed<T>::value
>     || is_unsigned<T>::value
>     || is_same<typename remove_cv<T>::type, bool>::value
>     || is_same<typename remove_cv<T>::type, char>::value
>     || is_same<typename remove_cv<T>::type, wchar_t>::value>
> { };

     template <class T>
     struct is_integral:
     integral_constant<
         bool,
            is_signed<T>::value
         || is_unsigned<T>::value
         || is_same<const volatile T, const volatile bool>::value
         || is_same<const volatile T, const volatile char>::value
         || is_same<const volatile T, const volatile wchar_t>::value> >
     { };

Martin

Re: type_traits is_integral implementation

Posted by Martin Sebor <se...@roguewave.com>.
Scott Zhong wrote:
> I try to implement type_traits using partial specialization as follows:
> 
> template <class T> struct __remove_s { typedef T type; };
> template <class T> struct __remove_s<signed T> { typedef T type; };
> template <class T> struct __remove_s<unsigned T> { typedef T type; };
> template <class T> struct __remove_s<short T> { typedef T type; };
> template <class T> struct __remove_s<long T> { typedef T type; };
> 
> template <class T> struct is_integral:
> 	__is_same<typename __remove_s<typename
> __remove_cv<T>::type>::type, int> { };
> 
> But signed is actually a type and T is already a type, and signed int is
> a compound type.

signed int is a fundamental type (3.9.1). It's also an arithmetic
type, but it's not a compound type (3.9.2). Precise terminology is
important when discussing traits :)

Unlike constness or volatility, signedness is a property that's
inherent to integer types. It's not something that can be removed
or added. The "signed" in, say, "signed long," is a part of the
name of the type, not just some attribute. Ditto for signed short
or signed int. Think of long, short, and int as aliases for the
verbose forms of their names.

> When compiling the above code in MSVC 8.0, it would
> give an error that states "too many template arguments".

That's a poor error message. Gcc says:

error: long, short, signed or unsigned invalid for 'type name'
error: partial specialization 'S<T>' does not specialize any template 
arguments

> I went ahead
> and read the book you had pointed to me, the implementation in there is
> using 3 enum? with a 3rd struct containing index_of template that would
> search though the enum with return value of greater than 0 if found, 0
> if not found.  I decided to implement it a different way as follows:
[...]
> Not really sure the internal workings of a template, does it treat each
> specialization as an enum? My implementation does generate 5 template
> instantiation whenever is_integral is called, would that adversely
> affect performance? Please let me know if there is a better
> implementation.

I would suggest you start by defining what an integer type is.
Writing down the definition will be helpful in breaking up the
implementation of the trait into smaller, more manageable bits.
Here's how section 3.9 of the working draft of the C++ standard
defines an integral type:

A C++ integral type is one of:
   bool, char, wchar_t, signed integer type, or unsigned integer
   type

A signed integer type is one of: standard signed integer type,
   or extended signed integer type

An unsigned integer type is one of: standard unsigned integer
   type or extended unsigned integer type

A standard signed/unsigned integer type is one of: short, int,
   long, long long unsigned short, unsigned, unsigned long, and
   unsigned long long

An extended signed/unsigned integer type is one of: a set of
   implementation-defined types

It should be relatively easy to model an implementation of the
trait based on the definition.

But before you start, since you already know that the trait (like
many others) is required to yield the same result when specialized
on a cv-qualified type as when specialized on a plain type, you
might want to create some simple helper traits that will let you
remove any cv-qualifications from types (remove_const,
remove_volatile, and remove_cv), or determine whether two types
are the same (is_same). These helper traits are also a required
part of the type traits interface (see 20.4.7) so by coding them
you'll be ahead of the game :)

Attached is my attempt at defining the helpers and the trait. Take
a look at it to see if it makes sense to you. As you can already
see there are a number of ways of implementing these critters. I
would encourage you to experiment with different approaches to see
which one you like best and how long each takes the compile with
which compiler. The goal should be to come up with an elegant,
compact implementation that's fast to compile and that generates
the least number of specializations of implementation classes
(deep template recursion is the enemy of compile time efficiency).

Martin