Skip to main content

enum, enum class and enum struct

In This section

Overview

An enumeration is a distinct type whose value is restricted to a range of values (see below for details), which may include several explicitly named constants ("enumerators"). The values of the constants are values of an integral type known as the underlying type of the enumeration. For more information about c++ enumeration see Enumeration declaration.

enum, enum class and enum struct in c++11 have two problems:

  • Displaying in a string the name of the enum value either for debugging or simply to qualify some values as for example in xtd::drawing::color::name.
  • There is no flags attribute. You have to add the bitwise operators for each enum, enum class or enum struct. Enum flags pose an additional problem for displaying the value names in a string due to bitwise flags.

External libraries

Several external libraries solve theses problems with elegance. Here is a non-exhaustive list:

In the context of xtd, after testing these different external libraries, none of them was chosen for various reasons such as limitations and others.

xtd has therefore its own enumeration management

This does not prevent you, of course, from using them in your own projects. As xtd is non-intrusive, you can easily combine them with xtd and with some xtd enum class definitions (if the limitations of the library do not prevent this).

Examples

The following code shows how to use enum class with magic_enum and xtd:

#include <magic_enum/magic_enum.hpp>
#include <xtd/xtd>

using namespace magic_enum;
using namespace xtd;

enum class enum_test {
value_one,
value_two,
value_three,
value_four
};

auto main()->int {
console::write_line("name = {}", enum_name<enum_test>(enum_test::value_four));
console::write_line("value = {}", enum_integer<enum_test>(enum_test::value_four));
console::write_line("as<int> = {}", as<int>(enum_test::value_four));
console::write_line("values = [{}]", ustring::join(", ", enum_values<enum_test>()));
console::write_line("names = {}", enum_names<enum_test>());
console::write("entries = [");
for (auto entry : enum_entries<enum_test>()) {
static auto index = 0;
console::write("{}({}, {})", index++ == 0 ? "" : ", ", as<int>(entry.first), entry.second);
}
console::write_line("]");
}

Output :

name = value_four
value = 3
as<int> = 3
values = [0, 1, 2, 3]
names = [value_one, value_two, value_three, value_four]
entries = [(0, value_one), (1, value_two), (2, value_three), (3, value_four)]

The following code shows how to use enum class with nav and xtd:

#include <nav/nav_core.hpp>
#include <xtd/xtd>

using namespace nav;
using namespace xtd;

nav_declare_enum(enum_test, int,
value_one,
value_two,
value_three,
value_four
);

auto main()->int {
console::write_line("name = {}", enum_test::value_four);
console::write_line("as<int> = {}", as<int>(enum_test::value_four));
console::write_line("values = {}", ustring::join(", ", enum_values<enum_test>));
console::write_line("names = {}", ustring::join(", ", enum_names<enum_test>));
console::write("entries = [");
for (size_t num_state = 0; num_state < num_states<enum_test>; ++num_state)
console::write("{}({}, {})", num_state == 0 ? "" : ", ", enum_values<enum_test>[num_state], enum_names<enum_test>[num_state]);
console::write_line("]");
}

Output :

name = 3
as<int> = 3
values = 0, 1, 2, 3
names = value_one, value_two, value_three, value_four
entries = [(0, value_one), (1, value_two), (2, value_three), (3, value_four)]

xtd::enum_register

xtd can introspect enum, enum class and enum struct with introspection helper macros or simply register the enum class with the xtd::enum_register class. The advantage is that it has no limitation and the disadvantage is that it is more verbose.

Note

If in your own project you do not register your enum class, there is no problem you can use it normally. But when you want to display the value of the enum class, it will always be displayed as an integer value and you can't do any parsing on it.

Examples

The following code shows how to use enum class with xtd::enum_register:

#include <xtd/xtd>

using namespace xtd;

enum class enum_test {
value_one,
value_two,
value_three,
value_four
};

template<> struct xtd::enum_register<enum_test> {
explicit operator auto() const noexcept {return xtd::enum_collection<enum_test> {{enum_test::value_one, "value_one"}, {enum_test::value_two, "value_two"}, {enum_test::value_three, "value_three"}, {enum_test::value_four, "value_four"}};}
};

auto main()->int {
console::write_line("name = {}", enum_test::value_four);
console::write_line("value = {}", enum_object(enum_test::value_four).to_int32());
console::write_line("as<int> = {}", as<int>(enum_test::value_four));
console::write_line("values = {}", enum_object<>::get_values<enum_test>());
console::write_line("names = {}", enum_object<>::get_names<enum_test>());
console::write("entries = [");
for (auto entry : enum_object<>::get_entries<enum_test>()) {
static auto index = 0;
console::write("{}({}, {})", index++ == 0 ? "" : ", ", as<int>(entry.first), entry.second);
}
console::write_line("]");
}

Output :

name = value_four
value = 3
as<int> = 3
values = [0, 1, 2, 3]
names = [value_one, value_two, value_three, value_four]
entries = [(0, value_one), (1, value_two), (2, value_three), (3, value_four)]

xtd::enum_set_attribute

The xtd::enum_set_attribute class is used to qualify an enum class attribute.

There are two types of attributes:

The class xtd::enum_set_attribute is only used when we want to qualify the enumeration with the attribute flags.

Write the following code to set the attribute of an enum class :

enum class enum_flags { /*...*/; }

template<> struct xtd::enum_set_attribute<enum_flags> {
explicit operator auto() const noexcept {return xtd::enum_attribute::flags;}
};

This does not dispense with writing the operators necessary to perform bitwise operations, but is necessary to display the values of the enum class correctly.

Let's take the following example and see what happens with and without setting the attribute flag to xtd::enum_attribute::flags :

enum class test_enum {
value_one = 0,
value_two = 1,
value_three = 2,
value_four = 4
};

test_enum operator|(test_enum lhs, test_enum rhs) {return static_cast<test_enum>(static_cast<std::underlying_type<test_enum>::type>(lhs) | static_cast<std::underlying_type<test_enum>::type>(rhs));}
#include <xtd/xtd.core.h>

using namespace xtd;

enum class test_enum {
value_one = 0,
value_two = 1,
value_three = 2,
value_four = 4
};

test_enum operator |(test_enum lhs, test_enum rhs) {return static_cast<test_enum>(static_cast<std::underlying_type<test_enum>::type>(lhs) | static_cast<std::underlying_type<test_enum>::type>(rhs));}

template<> struct xtd::enum_set_attribute<test_enum> {
explicit operator auto() const noexcept {return xtd::enum_attribute::flags;}
};

template<> struct xtd::enum_register<test_enum> {
explicit operator auto() const noexcept {return xtd::enum_collection<test_enum> {{test_enum::value_one, "value_one"}, {test_enum::value_two, "value_two"}, {test_enum::value_three, "value_three"}, {test_enum::value_four, "value_four"}};}
};

auto main()->int {
auto value = test_enum::value_two | test_enum::value_three;
console::write_line("value = {}", value);
}

output:

value = value_two, value_three

Ok, this is the expected value.

#include <xtd/xtd.core.h>

using namespace xtd;

enum class test_enum {
value_one = 0,
value_two = 1,
value_three = 2,
value_four = 4
};

test_enum operator |(test_enum lhs, test_enum rhs) {return static_cast<test_enum>(static_cast<std::underlying_type<test_enum>::type>(lhs) | static_cast<std::underlying_type<test_enum>::type>(rhs));}

template<> struct xtd::enum_register<test_enum> {
explicit operator auto() const noexcept {return xtd::enum_collection<test_enum> {{test_enum::value_one, "value_one"}, {test_enum::value_two, "value_two"}, {test_enum::value_three, "value_three"}, {test_enum::value_four, "value_four"}};}
};

auto main()->int {
auto value = test_enum::value_two | test_enum::value_three;
console::write_line("value = {}", value);
}

output:

value = 3

Error, this is not the expected value. Indeed, when the value is displayed, there is no member variable in enum_test that corresponds to 3.

flags_attribute_

To facilitate the writing of the flags attribute, there is the flags_attribute helper.

The flags_attribute helper helper sets the xtd::enum_set_attribute with the xtd::enum_attribute::flags atribute and implements the following operators for enum flags:

OperatorName
^=Bitwise XOR assignment
&=Bitwise AND assignment
|=Bitwise OR assignment
+=Addition assignment
-=Subtraction assignment
^Bitwise XOR
&Bitwise AND
|Bitwise OR
+Addition
-Subtraction
~Bitwise NOT

See operators for more information about operators.

The following code shows how to use flagsattribute helper.

#include <xtd/xtd.core.h>

using namespace xtd;

enum class test_enum {
value_one = 0,
value_two = 1,
value_three = 2,
value_four = 4
};

flags_attribute(, test_enum);

template<> struct xtd::enum_register<test_enum> {
explicit operator auto() const noexcept {return xtd::enum_collection<test_enum> {{test_enum::value_one, "value_one"}, {test_enum::value_two, "value_two"}, {test_enum::value_three, "value_three"}, {test_enum::value_four, "value_four"}};}
};

auto main()->int {
auto value = test_enum::value_two | test_enum::value_three;
console::write_line("value = {}", value);
}

output:

value = value_two, value_three

Warning

The flagsattribute helper has one limitiation :

  • The enum's flags cannot be in a class or struct. The enum must be in the global namespace or in a namespace hierarchy. If the enum flags is in a class or struct, add operators manually and use xtd::enum_set_attribute to register the xtd::enum_attribute::flags attribute.

Format

You can use the xtd::enum_object::to_string method to create a new string object that represents the numeric, hexadecimal, or string value of an enumeration member. This method takes one of the enumeration formatting strings to specify the value that you want returned.

See Enumeraton format strings for more information about enum class format.

The following code shows how to use enum class with format.

#include <xtd/xtd>

using namespace xtd;

enum class week_day {
monday,
tuesday,
wednesday,
thursday,
friday,
saturday,
sunday
};

template<> struct xtd::enum_register<week_day> {
explicit operator auto() const noexcept {return xtd::enum_collection<week_day> {{week_day::monday, "monday"}, {week_day::tuesday, "tuesday"}, {week_day::wednesday, "wednesday"}, {week_day::thursday, "thursday"}, {week_day::friday, "friday"}, {week_day::saturday, "saturday"}, {week_day::sunday, "sunday"}};}
};

auto main()->int {
console::write_line("{}", week_day::saturday);
console::write_line("0b{:b}", week_day::saturday);
console::write_line("0b{:B}", week_day::saturday);
console::write_line("{:d}", week_day::saturday);
console::write_line("{:D}", week_day::saturday);
console::write_line("{:g}", week_day::saturday);
console::write_line("{:G}", week_day::saturday);
console::write_line("0{:o}", week_day::saturday);
console::write_line("0{:O}", week_day::saturday);
console::write_line("0x{:x}", week_day::saturday);
console::write_line("0x{:X}", week_day::saturday);
}

output:

saturday
0b101
0b101
5
5
saturday
saturday
05
05
0x5
0x5

Note

xtd can display a string representing a member of an enum class as long as it is registered. If it is not registered then the numeric value will be displayed.

Parse

To parse an enum class with xtd, the enum class must be registered.

The folowwing example shows how to parse an enum class with xtd::enum_object::parse method.

#include <xtd/xtd>

using namespace xtd;

enum class enum_test {
value_one,
value_two,
value_three,
value_four
};

template<> struct xtd::enum_register<enum_test> {
explicit operator auto() const noexcept {return xtd::enum_collection<enum_test> {{enum_test::value_one, "value_one"}, {enum_test::value_two, "value_two"}, {enum_test::value_three, "value_three"}, {enum_test::value_four, "value_four"}};}
};

auto main()->int {
console::write_line("result = {}", enum_object<>::parse<enum_test>("value_two"));
console::write_line("result = {}", enum_object<>::parse<enum_test>("VaLuE_fOuR", true));
try {
console::write_line("result = {}", enum_object<>::parse<enum_test>("value_five"));
} catch(const xtd::system_exception& e) {
console::write_line("enum_test::value_five does not exists!");
}

enum_test result;
if (enum_object<>::try_parse<enum_test>("value_three", result)) console::write_line("result = {}", result);
if (enum_object<>::try_parse<enum_test>("vAlUe_OnE", true, result)) console::write_line("result = {}", result);
if (enum_object<>::try_parse<enum_test>("value_six", result)) console::write_line("result = {}", result);
else console::write_line("enum_test::value_six does not exists!");
}

Output :

result = value_two
result = value_four
enum_test::value_five does not exists!
result = value_three
result = value_one
enum_test::value_six does not exists!

Introspection

Registering an enum, enum class or an enum struct is unfortunately verbose.

There are some introspection helpers in xtd to facilitate the work:

Warning

The introspection helpers have one limitiation :

  • The enumeration cannot be in a class or struct. The enumeration must be in the global namespace or in a namespace hierarchy. If the enumumeration is in a class or struct, add operators manually and use xtd::enum_register to register the enumeration.

enum_

This helper provides the registration struct for enum.

Thr following example shows how to use enum_ helper.

#include <xtd/xtd>

using namespace xtd;

enum_(, enum_test,
value_one,
value_two,
value_three,
value_four
);

auto main()->int {
console::write_line("name = {}", enum_test::value_four);
console::write_line("value = {}", enum_object(enum_test::value_four).to_int32());
console::write_line("as<int> = {}", as<int>(enum_test::value_four));
console::write_line("values = {}", enum_object<>::get_values_as_int32<enum_test>());
console::write_line("names = {}", enum_object<>::get_names<enum_test>());
console::write_line("entries = {}", enum_object<>::get_entries_as_int32<enum_test>());
}

// This code produces the following output :
//
// name = value_four
// value = 3
// as<int> = 3
// values = [0, 1, 2, 3]
// names = [value_one, value_two, value_three, value_four]
// entries = [(0, value_one), (1, value_two), (2, value_three), (3, value_four)]

enum_ut_

This helper provides the registration struct for enum with specified underlying type.

Thr following example shows how to use enumut helper.

#include <xtd/xtd>

using namespace xtd;

enum_ut_(, enum_test, byte,
value_one,
value_two,
value_three,
value_four
);

auto main()->int {
console::write_line("name = {}", enum_test::value_four);
console::write_line("value = {}", enum_object(enum_test::value_four).to_byte());
console::write_line("as<byte> = {}", as<byte>(enum_test::value_four));
console::write_line("values = {}", enum_object<>::get_values_as_int32<enum_test>());
console::write_line("names = {}", enum_object<>::get_names<enum_test>());
console::write_line("entries = {}", enum_object<>::get_entries_as_int32<enum_test>());
}

// This code produces the following output :
//
// name = value_four
// value = 3
// as<byte> = 3
// values = [0, 1, 2, 3]
// names = [value_one, value_two, value_three, value_four]
// entries = [(0, value_one), (1, value_two), (2, value_three), (3, value_four)]

enum_class_

This helper provides the registration struct for enum class.

Thr following example shows how to use enumclass helper.

#include <xtd/xtd>

using namespace xtd;

enum_class_(, enum_test,
value_one,
value_two,
value_three,
value_four
);

auto main()->int {
console::write_line("name = {}", enum_test::value_four);
console::write_line("value = {}", enum_object(enum_test::value_four).to_int32());
console::write_line("as<int> = {}", as<int>(enum_test::value_four));
console::write_line("values = {}", enum_object<>::get_values_as_int32<enum_test>());
console::write_line("names = {}", enum_object<>::get_names<enum_test>());
console::write_line("entries = {}", enum_object<>::get_entries_as_int32<enum_test>());
}

// This code produces the following output :
//
// name = value_four
// value = 3
// as<int> = 3
// values = [0, 1, 2, 3]
// names = [value_one, value_two, value_three, value_four]
// entries = [(0, value_one), (1, value_two), (2, value_three), (3, value_four)]

enum_class_ut_

This helper provides the registration struct for enum class with specified underlying type.

Thr following example shows how to use enumclass_ut helper.

#include <xtd/xtd>

using namespace xtd;

enum_class_ut_(, enum_test, byte,
value_one,
value_two,
value_three,
value_four
);

auto main()->int {
console::write_line("name = {}", enum_test::value_four);
console::write_line("value = {}", enum_object(enum_test::value_four).to_byte());
console::write_line("as<byte> = {}", as<byte>(enum_test::value_four));
console::write_line("values = {}", enum_object<>::get_values_as_int32<enum_test>());
console::write_line("names = {}", enum_object<>::get_names<enum_test>());
console::write_line("entries = {}", enum_object<>::get_entries_as_int32<enum_test>());
}

// This code produces the following output :
//
// name = value_four
// value = 3
// as<byte> = 3
// values = [0, 1, 2, 3]
// names = [value_one, value_two, value_three, value_four]
// entries = [(0, value_one), (1, value_two), (2, value_three), (3, value_four)]

enum_struct_

This helper provides the registration struct for enum struct.

Thr following example shows how to use enumstruct helper.

#include <xtd/xtd>

using namespace xtd;

enum_class_(, enum_test,
value_one,
value_two,
value_three,
value_four
);

auto main()->int {
console::write_line("name = {}", enum_test::value_four);
console::write_line("value = {}", enum_object(enum_test::value_four).to_int32());
console::write_line("as<int> = {}", as<int>(enum_test::value_four));
console::write_line("values = {}", enum_object<>::get_values_as_int32<enum_test>());
console::write_line("names = {}", enum_object<>::get_names<enum_test>());
console::write_line("entries = {}", enum_object<>::get_entries_as_int32<enum_test>());
}

// This code produces the following output :
//
// name = value_four
// value = 3
// as<int> = 3
// values = [0, 1, 2, 3]
// names = [value_one, value_two, value_three, value_four]
// entries = [(0, value_one), (1, value_two), (2, value_three), (3, value_four)]

enum_struct_ut_

This helper provides the registration struct for enum struct with specified underlying type.

Thr following example shows how to use enumstruct_ut helper.

#include <xtd/xtd>

using namespace xtd;

enum_struct_ut_(, enum_test, byte,
value_one,
value_two,
value_three,
value_four
);

auto main()->int {
console::write_line("name = {}", enum_test::value_four);
console::write_line("value = {}", enum_object(enum_test::value_four).to_byte());
console::write_line("as<byte> = {}", as<byte>(enum_test::value_four));
console::write_line("values = {}", enum_object<>::get_values_as_int32<enum_test>());
console::write_line("names = {}", enum_object<>::get_names<enum_test>());
console::write_line("entries = {}", enum_object<>::get_entries_as_int32<enum_test>());
}

// This code produces the following output :
//
// name = value_four
// value = 3
// as<byte> = 3
// values = [0, 1, 2, 3]
// names = [value_one, value_two, value_three, value_four]
// entries = [(0, value_one), (1, value_two), (2, value_three), (3, value_four)]

See also