Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 1999-12-13 10:08:54


scleary_at_[hidden] wrote on 12/13/99 8:40 AM
>P.S. Howard - I like the compressed_pair name -- that's what empty_member
>has been evolving towards. Did you specialize call_traits/fundamentals for
>every standard builtin type?

Yes, I specialized type_traits on every builtin type and all 4 cv
variations thereof. I'll post it as an example of how I don't think it
should be done! :-)

// type_traits

// Using John's cv magic here

template <typename T> struct cv_traits_aux;

template <typename T>
struct cv_traits_aux<T*>
{
        typedef T cv_unqualified;
};

template <typename T>
struct cv_traits_aux<const T*>
{
        typedef T cv_unqualified;
};

template <typename T>
struct cv_traits_aux<volatile T*>
{
        typedef T cv_unqualified;
};

template <typename T>
struct cv_traits_aux<const volatile T*>
{
        typedef T cv_unqualified;
};

enum etype {integral_type, floating_type, void_type,
            array_type, pointer_type, reference_type,
            enum_type, class_type, union_type, member_ptr_type};

        template <typename T>
        struct type_traits
        {
                static const etype type = class_type;
                static const bool has_trivial_default = false;
                static const bool has_trivial_copy = false;
                static const bool has_trivial_assign = false;
                static const bool has_trivial_destructor = false;
                typedef cv_traits_aux<T*>::cv_unqualified cv_unqualified;
        };

        #define _MAKE_TYPE_TRAITS(t1, t2) \
        template <> \
        struct type_traits<t1> \
        { \
                static const etype type = t2; \
                static const bool has_trivial_default = true; \
                static const bool has_trivial_copy = true; \
                static const bool has_trivial_assign = true; \
                static const bool has_trivial_destructor = true; \
                typedef t1 cv_unqualified; \
        }; \
                                                              \
        template <> \
        struct type_traits<t1 const> \
        { \
                static const etype type = t2; \
                static const bool has_trivial_default = true; \
                static const bool has_trivial_copy = true; \
                static const bool has_trivial_assign = false; \
                static const bool has_trivial_destructor = true; \
                typedef t1 cv_unqualified; \
        }; \
                                                              \
        template <> \
        struct type_traits<t1 volatile> \
        { \
                static const etype type = t2; \
                static const bool has_trivial_default = true; \
                static const bool has_trivial_copy = true; \
                static const bool has_trivial_assign = true; \
                static const bool has_trivial_destructor = true; \
                typedef t1 cv_unqualified; \
        }; \
                                                              \
        template <> \
        struct type_traits<t1 const volatile> \
        { \
                static const etype type = t2; \
                static const bool has_trivial_default = true; \
                static const bool has_trivial_copy = true; \
                static const bool has_trivial_assign = false; \
                static const bool has_trivial_destructor = true; \
                typedef t1 cv_unqualified; \
        };

        _MAKE_TYPE_TRAITS(bool, integral_type)
        _MAKE_TYPE_TRAITS(char, integral_type)
        _MAKE_TYPE_TRAITS(signed char, integral_type)
        _MAKE_TYPE_TRAITS(unsigned char, integral_type)
...

        #undef _MAKE_TYPE_TRAITS

        template <typename T>
        struct type_traits<T*>
        {
                static const etype type = pointer_type;
                static const bool has_trivial_default = true;
                static const bool has_trivial_copy = true;
                static const bool has_trivial_assign = true;
                static const bool has_trivial_destructor = true;
                typedef T* cv_unqualified;
        };

        template <typename T>
        struct type_traits<T* const>
        {
                static const etype type = pointer_type;
                static const bool has_trivial_default = true;
                static const bool has_trivial_copy = true;
                static const bool has_trivial_assign = false;
                static const bool has_trivial_destructor = true;
                typedef T* cv_unqualified;
        };
...
        template <typename T>
        struct type_traits<T&>
        {
                static const etype type = reference_type;
                static const bool has_trivial_default = false;
                static const bool has_trivial_copy = true;
                static const bool has_trivial_assign = false;
                static const bool has_trivial_destructor = true;
                typedef T& cv_unqualified;
        };

        template <typename T>
        struct type_traits<T& const>
        {
                static const etype type = reference_type;
                static const bool has_trivial_default = false;
                static const bool has_trivial_copy = true;
                static const bool has_trivial_assign = false;
                static const bool has_trivial_destructor = true;
                typedef T& cv_unqualified;
        };
...
        template <typename T, _STD::size_t sz>
        struct type_traits<T[sz]>
        {
                static const etype type = array_type;
                static const bool has_trivial_default = true;
                static const bool has_trivial_copy = false;
                static const bool has_trivial_assign = false;
                static const bool has_trivial_destructor = true;
                typedef T cv_unqualified[sz];
        };

        template <typename T, _STD::size_t sz>
        struct type_traits<const T[sz]>
        {
                static const etype type = array_type;
                static const bool has_trivial_default = false;
                static const bool has_trivial_copy = false;
                static const bool has_trivial_assign = false;
                static const bool has_trivial_destructor = true;
                typedef T cv_unqualified[sz];
        };
...

Once all this is in place, call_traits isn't too bad and works for cv
qualified types. I was going to hold off on submitting it till the
"fundamentals" were in place, but here is how I've got it today:

template <typename T>
struct call_trait_aux
{
        static const bool is_small_and_fast = type_traits<T>::has_trivial_copy
                                           && sizeof(T) <= sizeof(void*);
        static const bool is_specialized = type_traits<T>::type == integral_type
                                        || type_traits<T>::type == floating_type
                                        || type_traits<T>::type == reference_type
                                        || type_traits<T>::type == array_type;
};

template <typename T, bool PassByValue =
call_trait_aux<T>::is_small_and_fast
                                      ||
call_trait_aux<T>::is_specialized>
struct call_traits
{
        typedef T value_type;
        typedef T& reference;
        typedef const T& const_reference;
        typedef const_reference param_type;
};

template <typename T>
struct call_traits<T&, true>
{
        typedef T value_type;
        typedef T& reference;
        typedef const T& const_reference;
        typedef reference param_type;
};

template <typename T>
struct call_traits<T& const, true>
{
        typedef T value_type;
        typedef T& const reference;
        typedef const T& const const_reference;
        typedef reference param_type;
};

template <typename T>
struct call_traits<T& volatile, true>
{
        typedef T value_type;
        typedef T& volatile reference;
        typedef const T& volatile const_reference;
        typedef reference param_type;
};

template <typename T>
struct call_traits<T& const volatile, true>
{
        typedef T value_type;
        typedef T& const volatile reference;
        typedef const T& const volatile const_reference;
        typedef reference param_type;
};

template <typename T, _CSTD::size_t sz>
struct call_traits<T[sz], true>
{
        typedef const T* value_type;
        typedef T(&reference)[sz];
        typedef const T(&const_reference)[sz];
        typedef value_type param_type;
};

template <typename T, _CSTD::size_t sz>
struct call_traits<const T[sz], true>
{
        typedef const T* value_type;
        typedef const T(&reference)[sz];
        typedef const T(&const_reference)[sz];
        typedef value_type param_type;
};

template <typename T, _CSTD::size_t sz>
struct call_traits<volatile T[sz], true>
{
        typedef const volatile T* value_type;
        typedef volatile T(&reference)[sz];
        typedef const volatile T(&const_reference)[sz];
        typedef value_type param_type;
};

template <typename T, _CSTD::size_t sz>
struct call_traits<const volatile T[sz], true>
{
        typedef const volatile T* value_type;
        typedef const volatile T(&reference)[sz];
        typedef const volatile T(&const_reference)[sz];
        typedef value_type param_type;
};

template <typename T>
struct call_traits<T, true>
{
        typedef T value_type;
        typedef T& reference;
        typedef const T& const_reference;
        typedef T const param_type;
};

>I've been trying to get a specialization
>working that would allow us to determine if a type is empty or not, but
>haven't gotten anything to work. . .

Your is_empty is easy to extend now (except of course that enums break
it):

template <typename T>
struct can_derive_from
{
        static const bool value = type_traits<T>::type == class_type
                               || type_traits<T>::type == union_type;
};

template <typename T, bool b = can_derive_from<T>::value>
struct is_empty_imp
        : T
{
        int data_;
};

template <typename T>
struct is_empty_imp<T, false>
{
        T data1_;
        int data2_;
};

template <typename T>
struct is_empty
{
        static const bool value = sizeof(is_empty_imp<T>) == sizeof(int);
};

#include <iostream>

struct A
{
};

int main()
{
        std::cout << is_empty<A>::value << '\n';
        std::cout << is_empty<A&>::value << '\n';
        std::cout << is_empty<int>::value << '\n';
}

-Howard


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