
Boost : 
From: John Maddock (John_Maddock_at_[hidden])
Date: 19991205 08:06:41
Its become apparent to me that the version of SGI's type_traits.h that I
have been using is not sufficiently flexible for my needs  Steve's
proposal for typetraits gets much closer to the mark, but I can't help
wondering if we can get closer still.
I've been trying to figure out what the minimal requirement for some sort
of type traits concept should be, in fact some generic design rules for
traits classes in general, I'm not sure if this is complete but here goes
anyway:
There are three categories of traits class, which can be described as
follows:
1) Property traits:
Does some type T have some property P? Generally expressed as:
template <typename T>
struct P { static const bool value = true/false; }
Property traits are in general the most fundamental of all traits types 
user defined propertytraits should never have to specialise for builtin
types  they should instead be implemented "in terms of" some fundamental
set of property traits (whatever these may be  see later).
2) Transformation traits:
Given some Type/Value as input, transform it to some other Type/value
according to some well defined rule:
template <typename T>
struct rule{ typedef application_of_rule type; }
Like property traits, there is probably some fundamental set of
transformations from which almost all others can be synthesised, again like
property traits it should not be necessary to specialise these for all
builtin types.
3) Baggage classes:
A baggage class applies some customization to a template class, by
supplying the baggage class as a template parameter to the client class. An
example would be std::char_traits<>. In general these should not need to
be specialised for builtins (although they often are), but rather
generically programmed in terms of standard property and transformation
traits.
*****
OK lets suppose that some "fundamental set" of property/transformation
traits does in fact exist, there are a couple of design goals we should try
to obey:
A) No two traits classes should duplicate the same functionality  using
the definitions above, that should be easy to achieve since each class
effectively concentrates on just one task. However we already have some
traits classes in the standard library  and since we're not about to
rewrite that, let's assume that std::numeric_limits<T> is a "fundamental"
property traits class.
B) We should minimise the number of traits class specialisations in the
library  by keeping the fundamental set as small as possible  and
implementing any other traits classes in terms of that set.
So the question becomes  what fundamental questions can we ask about a
type?
The following list seems to cover most bases  those marked * are
fundamental (not implemented in terms of another class), those marked ?
seem to be nonimplementable.
* is a type T a built in type  is_builtin<T>
is a type T an arithmetic/integral/real type  is_signed_int<T>
is_unsigned_int<T> is_int<T> is_real<T>  implemented in terms of
numeric_limits<T>.
* is a type T a reference type  is_reference<T>
* is a type T a pointer type (including function pointers)  is_pointer<T>
*? is a type T a regular function pointer  is_function_pointer<T>
* is a type T a member function pointer  is_member_pointer<T>
* is a type T declared const  is_const<T>
* is a type T declared volatile  is_volatile<T>
* is a type T an array  is_array<T>
* is a type T the same as type U  is_same_type<T,U>
is a type T a UDT  is_UDT<T>  the inverse of is_builtin<T>, maybe
unnecessary.
*? is a type T an enum  is_enum<T>
*? is a type T a composite type (class/struct/union)  is_composite<T> 
can be implemented if is_enum<T> can be (and viceversa).
* is a type T an empty composite type  is_empty<T>  only strictly
implementable if we have is_composite<T>
is a type T a POD type  is_POD<T>  by default implemented in terms of
is_builtin<T>  can be specialised for UDT's.
The implementation of the above is either obvious or follows Steve's code:
except for is_const and is_volatile  code for which follows. Those
marked ? pose more of a problem, any ideas gratefully received, also are
there any fundamental type properties that are missing?
We can also construct a similar list for type transformations:
* convert a type T to a reference if its not already one  add_reference<T>
* convert a type T to a nonreference if it is one  remove_reference<T>
* convert a type T to nonconst type  remove_const<T>
* convert a type T to a nonvolatile type  remove_volatile<T>
convert a type T to a noncvqualified type  remove_qualification<T>
Again code follows Steve's except for the cvqualifier code which follows,
note that there are no add_const or add_volatile templates as these are in
effect trivial. Again this is not necessarily a complete list.
I'm fairly sure that an implementation as above would simplify the
implementation of call_traits, unfortunately it doesn't quite provide what
Howard wanted for empty_member  because is_composite<T> appears not to be
implementable. It would also be of great help to me in simplifying some of
the baggage classes that I'm currently working with, but what about every
one else? What have I missed? Or would anyone else use this?
 John.
Code for cvmanipulation:
namespace _Jm{
//
// implementation helper:
//
template <class T>
struct cv_traits_imp{};
template <class T>
struct cv_traits_imp<T*>
{
static const bool is_const = false;
static const bool is_volatile = false;
typedef T non_const_type;
typedef T non_volatile_type;
typedef T unqualified_type;
static const char* what() { return ""; }
};
template <class T>
struct cv_traits_imp<const T*>
{
static const bool is_const = true;
static const bool is_volatile = false;
typedef T non_const_type;
typedef T non_volatile_type;
typedef T unqualified_type;
static const char* what() { return "const"; }
};
template <class T>
struct cv_traits_imp<volatile T*>
{
static const bool is_const = false;
static const bool is_volatile = true;
typedef T non_const_type;
typedef T non_volatile_type;
typedef T unqualified_type;
static const char* what() { return "volatile"; }
};
template <class T>
struct cv_traits_imp<const volatile T*>
{
static const bool is_const = true;
static const bool is_volatile = true;
typedef volatile T non_const_type;
typedef const T non_volatile_type;
typedef T unqualified_type;
static const char* what() { return "const volatile"; }
};
} // namespace _Jm
template <typename T>
struct is_const
{
static const bool value = _Jm::cv_traits_imp<T*>::is_const;
};
template <typename T>
struct is_volatile
{
static const bool value = _Jm::cv_traits_imp<T*>::is_volatile;
};
template <typename T>
struct remove_volatile
{
typedef typename _Jm::cv_traits_imp<T*>::non_volatile_type type;
};
template <typename T>
struct remove_const
{
typedef typename _Jm::cv_traits_imp<T*>::non_const_type type;
};
template <typename T>
struct remove_qualification
{
typedef typename _Jm::cv_traits_imp<T*>::unqualified_type type;
};
//
// test code:
//
int main()
{
cout << is_const<int>::value << endl;
cout << is_const<const int>::value << endl;
cout << is_const<volatile int>::value << endl;
cout << is_const<const volatile int>::value << endl;
cout << is_volatile<int>::value << endl;
cout << is_volatile<const int>::value << endl;
cout << is_volatile<volatile int>::value << endl;
cout << is_volatile<const volatile int>::value << endl;
}
//
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk