Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is_enum implementation

Tags:

c++

enums

c++11

I'm trying to implement std::is_enum. Here is my code so far:

template<typename T>
struct is_enum {
    static bool value;
};

template<typename T>
bool is_enum<T>::value = false;

template<enum E>
struct is_enum {
    static bool value;
};

template<enum E>
bool is_enum<E>::value = true;

This code causes error. More precisely:

g++ -std=c++0x -Wall -o "enum2" "enum2.cpp" (in directory: /home/aristophanes/Desktop/C++)
Compilation failed.
enum2.cpp:11:15: error: use of enum ‘E’ without previous declaration
enum2.cpp:3:10: error: template parameter ‘class T’
enum2.cpp:12:8: error: redeclared here as ‘int E’
enum2.cpp:16:15: error: use of enum ‘E’ without previous declaration
enum2.cpp:17:14: error: ‘E’ was not declared in this scope
enum2.cpp:17:15: error: template argument 1 is invalid
enum2.cpp:17:18: error: template declaration of ‘bool value’

Can anyone explain to me where I make a mistake? It is mine or the compiler's fault? Thanks in advance.

Edit: if it is completely wrong, then how can I correct it?

Note: I'm using g++ -o <file> <file>.cpp

like image 242
Rontogiannis Aristofanis Avatar asked Jul 03 '12 18:07

Rontogiannis Aristofanis


3 Answers

The best way to implement this is to use compiler magic, and I believe most implementations do this.

For example, here's libc++'s implementation for gcc >= 4.3 and any compiler that __has_feature(is_enum)1

template <class _Tp> struct _LIBCPP_VISIBLE is_enum
    : public integral_constant<bool, __is_enum(_Tp)> {};



For all other compilers libc++ does:

template <class _Tp> struct _LIBCPP_VISIBLE is_enum
    : public integral_constant<bool, !is_void<_Tp>::value             &&
                                     !is_integral<_Tp>::value         &&
                                     !is_floating_point<_Tp>::value   &&
                                     !is_array<_Tp>::value            &&
                                     !is_pointer<_Tp>::value          &&
                                     !is_reference<_Tp>::value        &&
                                     !is_member_pointer<_Tp>::value   &&
                                     !is_union<_Tp>::value            &&
                                     !is_class<_Tp>::value            &&
                                     !is_function<_Tp>::value         > {};

Some of those other type traits still require compiler magic.2 E.g. is_union. However, that condition can be rewritten such that it doesn't need compiler magic. This can be done by replacing the seperate checks for unions and classes with a single check for both, as Johannes Schaub points out.

1. So far as I know only clang implements __has_feature, unfortunately.
2. It's interesting that libc++ does have a version of is_union<T> and is_class<T> that do not use compiler intrinsics, but as a result they provide erroneous results for union types. But their erroneous results are complementary so libc++'s fallback implementation of is_enum<T> provides accurate results.

like image 66
bames53 Avatar answered Nov 07 '22 23:11

bames53


This

template<enum E>

promises that the template argument is a value of type enum E. The argument is NOT a type (Type template arguments are introduced by typename, or for backward compatibility, class. Even struct isn't allowed). It's just like saying

template<int i>

except no name is given for the variable.

Things go wrong from there.

like image 45
Ben Voigt Avatar answered Nov 07 '22 23:11

Ben Voigt


You problem is that

template<enum E>

Is interpreted as unnamed parameter with type forward declared enum named E.
Semantically same to

template<int>

Just substituting int with enum E.

like image 2
Dani Avatar answered Nov 08 '22 01:11

Dani