Adi
Adi

Reputation: 2051

How can I output the value of an enum class in C++11

How can I output the value of an enum class in C++11? In C++03 it's like this:

#include <iostream>

using namespace std;

enum A {
  a = 1,
  b = 69,
  c= 666
};

int main () {
  A a = A::c;
  cout << a << endl;
}

in c++0x this code doesn't compile

#include <iostream>

using namespace std;

enum class A {
  a = 1,
  b = 69,
  c= 666
};

int main () {
  A a = A::c;
  cout << a << endl;
}


prog.cpp:13:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&'
/usr/lib/gcc/i686-pc-linux-gnu/4.5.1/../../../../include/c++/4.5.1/ostream:579:5: error:   initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = A]'

compiled at Ideone.com

Upvotes: 144

Views: 207184

Answers (10)

kuga
kuga

Reputation: 1755

I am surprised no one had mentioned magic_enum before. It provides stream operators and prints the name string instead of the integer value:

#include <magic_enum/magic_enum_iostream.hpp>
#include <iostream>

using magic_enum::iostream_operators::operator<<; // out-of-the-box ostream operators for enums..
int main()
{
    enum class Color { RED, GREEN, BLUE};
    Color c = Color::BLUE;
    std::cout << c << '\n';
}

See on godbolt.

Note: The author requested a solution for C++11, but magic_enum is C++17. Nevertheless, I think this answer is valid in 2024 :)

Upvotes: 1

aydwi
aydwi

Reputation: 25

Extending on the excellent answer by James McNellis, if your compiler has support for Concepts and Constraints (introduced in C++20), it could be used to introduce additional compile-time sanity (as in, clearer indication of any incorrect usage) to the function template in the following straighforward manner:

template<typename E>
concept EnumType = std::is_enum_v<E>;

template <EnumType E>
constexpr std::underlying_type_t<E> enumUnderlyingType(E const underlying_type)
{
    return static_cast<std::underlying_type_t<E>>(underlying_type);
}

Upvotes: 1

RiaD
RiaD

Reputation: 47619

Since c++23 there is

std::to_underlying which does the same thing as in all the other answer but it's in std

Upvotes: 3

Earl of Lemongrab
Earl of Lemongrab

Reputation: 305

You could do something like this:

//outside of main
namespace A
{
    enum A
    {
        a = 0,
        b = 69,
        c = 666
    };
};

//in main:

A::A a = A::c;
std::cout << a << std::endl;

Upvotes: -2

NutCracker
NutCracker

Reputation: 12253

Following worked for me in C++11:

template <typename Enum>
constexpr typename std::enable_if<std::is_enum<Enum>::value,
                                  typename std::underlying_type<Enum>::type>::type
to_integral(Enum const& value) {
    return static_cast<typename std::underlying_type<Enum>::type>(value);
}

Upvotes: 3

Audrius Meškauskas
Audrius Meškauskas

Reputation: 21728

To write simpler,

enum class Color
{
    Red = 1,
    Green = 11,
    Blue = 111
};

int value = static_cast<int>(Color::Blue); // 111

Upvotes: 23

yau
yau

Reputation: 555

(I'm not allowed to comment yet.) I would suggest the following improvements to the already great answer of James McNellis:

template <typename Enumeration>
constexpr auto as_integer(Enumeration const value)
    -> typename std::underlying_type<Enumeration>::type
{
    static_assert(std::is_enum<Enumeration>::value, "parameter is not of type enum or enum class");
    return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}

with

  • constexpr: allowing me to use an enum member value as compile-time array size
  • static_assert+is_enum: to 'ensure' compile-time that the function does sth. with enumerations only, as suggested

By the way I'm asking myself: Why should I ever use enum class when I would like to assign number values to my enum members?! Considering the conversion effort.

Perhaps I would then go back to ordinary enum as I suggested here: How to use enums as flags in C++?


Yet another (better) flavor of it without static_assert, based on a suggestion of @TobySpeight:

template <typename Enumeration>
constexpr std::enable_if_t<std::is_enum<Enumeration>::value,
std::underlying_type_t<Enumeration>> as_number(const Enumeration value)
{
    return static_cast<std::underlying_type_t<Enumeration>>(value);
}

Upvotes: 10

James Adkison
James Adkison

Reputation: 9602

It is possible to get your second example (i.e., the one using a scoped enum) to work using the same syntax as unscoped enums. Furthermore, the solution is generic and will work for all scoped enums, versus writing code for each scoped enum (as shown in the answer provided by @ForEveR).

The solution is to write a generic operator<< function which will work for any scoped enum. The solution employs SFINAE via std::enable_if and is as follows.

#include <iostream>
#include <type_traits>

// Scoped enum
enum class Color
{
    Red,
    Green,
    Blue
};

// Unscoped enum
enum Orientation
{
    Horizontal,
    Vertical
};

// Another scoped enum
enum class ExecStatus
{
    Idle,
    Started,
    Running
};

template<typename T>
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
{
    return stream << static_cast<typename std::underlying_type<T>::type>(e);
}

int main()
{
    std::cout << Color::Blue << "\n";
    std::cout << Vertical << "\n";
    std::cout << ExecStatus::Running << "\n";
    return 0;
}

Upvotes: 25

ForEveR
ForEveR

Reputation: 55887

#include <iostream>
#include <type_traits>

using namespace std;

enum class A {
  a = 1,
  b = 69,
  c= 666
};

std::ostream& operator << (std::ostream& os, const A& obj)
{
   os << static_cast<std::underlying_type<A>::type>(obj);
   return os;
}

int main () {
  A a = A::c;
  cout << a << endl;
}

Upvotes: 51

James McNellis
James McNellis

Reputation: 355019

Unlike an unscoped enumeration, a scoped enumeration is not implicitly convertible to its integer value. You need to explicitly convert it to an integer using a cast:

std::cout << static_cast<std::underlying_type<A>::type>(a) << std::endl;

You may want to encapsulate the logic into a function template:

template <typename Enumeration>
auto as_integer(Enumeration const value)
    -> typename std::underlying_type<Enumeration>::type
{
    return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}

used as:

std::cout << as_integer(a) << std::endl;

Upvotes: 183

Related Questions