Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Metaprogramming construct that returns underlying type for an enum and integer for an iteger

I need to write a metaprogramming construct, that when given an enum type, it returns an underlying type of that enum, but when given an integer, it returns that integer.

For example:

enum Enum : short { VALUE1, VALUE2 };

int_type<long>::type  // -> long
int_type<Enum>::type  // -> short

I attempted this

template< typename Type >
struct int_type {
    using type = typename std::enable_if< std::is_enum<Type>::value, typename std::underlying_type<Type>::type >::type;
};
template< typename Type >
struct int_type {
    using type = typename std::enable_if< std::is_integral<Type>::value, Type >::type;
};

but it complains about redefinition of the struct.

I also tried this, but

template< typename Type >
struct int_type {
    using type = typename std::enable_if< std::is_enum<Type>::value, typename std::underlying_type<Type>::type >::type;
    using type = typename std::enable_if< std::is_integral<Type>::value, Type >::type;
};

but then it complains about redefinition of member type.

This is where my metaprogramming skills end, can anyone help?

EDIT: I should have also mentioned, that our project is limited to C++11.

like image 310
Youda008 Avatar asked Jan 27 '23 02:01

Youda008


2 Answers

The problem with your attempts is that you're defining the same thing twice, and although part of it uses enable_if that doesn't make the entire enclosing definition disabled when the enable_if isn't true. i.e. you have two definitions of int_type::type even when one (or both) of those definitions is invalid.

What you're looking for is std::conditional:

template< typename Type >
struct int_type {
    using when_enum
      = std::underlying_type<Type>;
    using when_integral
      = std::enable_if<std::is_integral<Type>::value, Type>;
    using type
        = typename std::conditional< std::is_enum<Type>::value,
            when_enum, when_integral
            >::type::type;
};

Or in C++14 and later:

template< typename Type >
struct int_type {
    using when_enum
      = std::underlying_type<Type>;
    using when_integral
      = std::enable_if<std::is_integral<Type>::value, Type>;
    using type
        = typename std::conditional_t< std::is_enum<Type>::value,
            when_enum, when_integral
            >::type;
};

But I think I'd probably write it as an alias template:

template< typename Type >
  using int_type = std::conditional_t<
    std::is_enum<Type>::value,
    std::underlying_type<Type>,
    std::enable_if<std::is_integral<Type>::value, Type>>;

And maybe simplify it further and make int_type<T> an alias for the actual type, so you don't need to say typename int_type<T>::type:

template< typename Type >
  using int_type = typename std::conditional_t<
    std::is_enum<Type>::value,
    std::underlying_type<Type>,
    std::enable_if<std::is_integral<Type>::value, Type>>::type;

(N.B. In C++17 you can use std::is_enum_v<Type> and std::is_integral_v instead of saying is_xxx<Type>::value.)

like image 130
Jonathan Wakely Avatar answered Jan 28 '23 15:01

Jonathan Wakely


You can do it with partial specializations.

// primary template
template< typename Type, typename = void >
struct int_type {
};

// partial specialization for enum types
template< typename Type >
struct int_type <Type, typename std::enable_if< std::is_enum<Type>::value >::type > {
    using type = typename std::underlying_type<Type>::type;
};

// partial specialization for integral types
template< typename Type>
struct int_type <Type, typename std::enable_if< std::is_integral<Type>::value >::type > {
    using type = Type;
};

LIVE

like image 28
songyuanyao Avatar answered Jan 28 '23 15:01

songyuanyao