views:

85

answers:

2

I need to define a template struct such that:

element<T>::type

is of type:

T::element_type 

if T contains a (public) typedef named element_type, otherwise (if it does not contain such typedef)

element<T>::type

is of type

T::value_type 

if T is mutable and of type

const T::value_type

if T is constant.

I am really struggling with this, any suggestion is very appreciated! :)

Thank you very much for your help in advance!

+2  A: 

Maybe something like:

template <typename T>
struct has_element_type
{
    typedef char yes[1];
    typedef char no[2];

    template <typename C>
    static yes& test(typename C::element_type*);

    template <typename>
    static no& test(...);

    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

template <typename T>
struct is_const
{
    static const bool value = false;
};


template <typename T>
struct is_const<const T>
{
    static const bool value = true;
};

template <typename, bool> // true -> const
struct value_type_switch; 

template <typename T>
struct value_type_switch<T, true>
{
    typedef const typename T::value_type type;
};

template <typename T>
struct value_type_switch<T, false>
{
    typedef typename T::value_type type;
};

template <typename, bool> // true -> has element_type
struct element_type_switch;

template <typename T>
struct element_type_switch<T, true>
{
    typedef typename T::element_type type;
};


template <typename T>
struct element_type_switch<T, false>
{
    typedef typename value_type_switch<T, is_const<T>::value>::type type;
};

template <typename T>
struct element
{
    typedef typename element_type_switch<T,
                                    has_element_type<T>::value>::type type;
};

This should of course be split up and organized.

GMan
@Gman: in struct element shouldn't it be `typedef typename element_type_switch<T, has_element_type<T>::value>::type type;` instead of `typedef typename element_type_switch<T, has_element_type<T>::value> type;` ? the same in element_type_switch...
Armen Tsirunyan
@Armen: Ya, typo. Thanks.
GMan
I tested it and it works! Thank you very much. I have a small preference for the solution proposed by Potatoswapper (more concise and *to me* easier to understand), but I have trouble to get it compiling... Thank you all, too bad I cannot assign an answer to both of you because you would deserve it! :)
KRao
+1  A: 

As an alternative to declaring a traits class using SFINAE, you can use it more subtly with partial specialization.

template< typename T >
struct empty { // support class is like stripped-down enable_if
    typedef void type;
};

template< class T, typename v = void > // v is always void!
struct element {
    typedef typename T::value_type type;
};

template< class T, typename v >
struct element< T const, v > {
    typedef typename T::value_type const type;
};

template< class T > // T in deduced context, T::element_type is SFINAE:
struct element< T, typename empty< typename T::element_type >::type > {
    typedef typename T::element_type type;
};

… you might want to add another case to make element_type const for const T? Unfortunately this doesn't work in GCC, although Comeau accepts it.

template< class T >
struct element< T const, typename empty< typename T::element_type >::type > {
    typedef typename T::element_type const type;
};

Code I used to test this:

struct has_et {
    typedef int element_type;
};

struct has_vt {
    typedef char value_type;
};

char c;
int i;

element<has_vt>::type *cp = &c;
element<has_et>::type *ip = &i;
Potatoswatter
For those interested in the bug, http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46105
Potatoswatter
I tried this (with VS2010) but I have problem: it works perfectly for element<has_vt>, element<const has_vt> and element<has_et>, but element<const has_et> fails to compile (with and without the last additional specialisation) complaining about more then 1 matching partial specialisation. I would like element<const has_et>::type to be has_et::element_type. Thank you again for your help, very appreciated! Do you think this issue can be solved?
KRao
@KRao: Hmm, I tried fiddling around with it and I can't get GCC or Comeau to accept `const has_et` as an argument. Guess I should've tested better :vP . Indeed the "additional" specialization is required for correctness, but as far as I can tell it should work properly as long as you include it. (You can erase the `const` in its typedef, or have it derive from the non-const specialization, but it must exist.) Maybe I should put this code in a new SO question, since I'm so sure it's correct… In the meantime, I think GMan's solution is the safe choice.
Potatoswatter
@KRao: Huh… actually it turns out Comeau does accept this code and handle all 4 cases perfectly fine. I was fiddling around too much before. So, "someday," you can use this more elegant form.
Potatoswatter
Thank you for your investigation :)
KRao