Creating an
However, this can get troublesome when name and scope resolution start to play a part in your program. Declaring the above
This fine and well, but what if we want 2 separate
The above code won't compile as there's a redefinition of
Enter the
The Enumeration Class Objects In Omni
Throughout the library there are various
The
The
Other functionality of the
Here's an example using the
The
It's possible to use a
To over come this, the
To define a simple
The
For a simple example, take the following code to create a couple of
Note that if you are using the
A few additional notes about these macros; if you are using the
This code will create an
The last macro that can be utilized to create an
Here is a simple example to create an
The
For more information on each of the macro's and how to utilize them, you can click on any of them throughout this example.
Example
enum type is simple enough in C++, simply declare one and put your values in it, for example:enum alphabet {
A,
B,
C
}enum type in the global scope of a program, then allows you to call the values in the enum directly. Example:enum alphabet {
A,
B,
C
}
void some_function(alphabet val)
{
switch (val) {
case A: std::cout << "A" << std::endl; break;
case B: std::cout << "B" << std::endl; break;
case C: std::cout << "C" << std::endl; break;
default: std::cout << "?" << std::endl; break;
}
}enum types with similar named values, but different underlying integral value, like so:enum alphabet {
A = 1,
B = 2,
C = 3
}
enum action_plan {
A = 100,
B = 1000,
C = 10000
}
void some_function(alphabet val)
{
switch (val) {
case A: std::cout << "A" << std::endl; break;
case B: std::cout << "B" << std::endl; break;
case C: std::cout << "C" << std::endl; break;
default: std::cout << "?" << std::endl; break;
}
}
void some_function(action_plan val)
{
switch (val) {
case A: std::cout << "Plan A" << std::endl; break;
case B: std::cout << "Plan B" << std::endl; break;
case C: std::cout << "Plan C" << std::endl; break;
default: std::cout << "?" << std::endl; break;
}
}A and the other enum values. Additionally, a basic enum doesn't allow you to have a well-defined default type, nor can you easily know the number of values in the enum itself (something other languages, like C# or Java, have capabilities for).Enter the
enum class wrapper.The Enumeration Class Objects In Omni
Throughout the library there are various
enum types that are used to specify various running aspects of your code; for example, if you create an omni::sync::basic_thread you can specify to have it start later (i.e. you specifically call the omni::sync::basic_thread::start method yourself). To do this, you specify the omni::sync::thread_start_type::USER value when creating the thread.The
thread_start_type class has an underlying enum definition and various operators that allows the class to act like a wrapper type for the enum.The
enum wrapper classes that exists throughout the library allow you to define enum types that can be treated much like you treat any other basic integral data type, but with additional benefits of parsing and other type-safety checks. As well, since the wrapper classes only storage member is an enum type that is within the class, the sizeof the classes are equal to the sizeof the enum type itself, making the classes extremely lightweight.Other functionality of the
enum wrapper classes include printing of the value as a string-ized version of the enum value (e.g. omni::sync::thread_start_type::USER will print out simply USER instead of the numeric value). Additionally, each wrapper class has a COUNT and count function that both return the number of values in the enum type, as well as a DEFAULT_VALUE function that returns the default enumeration value for an enum created without being assigned to, and string parsing and printing functionality along with assignment and equality operators that allow you to use the class like a basic integer type.Here's an example using the
omni::net::protocol_type wrapper class to do various processing of the enum value:std::cout << "Count of enum values: " << omni::net::protocol_type::COUNT() << std::endl; std::cout << "Default enum value: " << omni::net::protocol_type::DEFAULT_VALUE() << std::endl; omni::net::protocol_type type; std::cout << "Current protocol type: " << type << std::endl; // will print the default value type = omni::net::protocol_type::TCP; std::cout << "Current protocol type: " << type << std::endl; // will print "TCP" std::cout << "Current protocol type int value: " << type.value() << std::endl; // will print the numeric value type = omni::net::protocol_type::parse("UDP"); // Since omni::net::protocol_type::UDP exists, this will // assign omni::net::protocol_type::UDP to type std::cout << "Current protocol type: " << type << std::endl; // will print "UDP" // test to see if a number is a valid if (omni::net::protocol_type::is_valid(42)) { std::cout << "42 is a valid numeric value that can be represented as an enum" << std::endl; } else { std::cout << "42 is not a valid numeric value that can be represented as an enum" << std::endl; } // test if a string value is valid if (!omni::net::protocol_type::try_parse("INET", type)) { std::cout << "INET is not a valid protocol type" << std::endl; } std::cout << "Current protocol type: " << type << std::endl; // will print the default value // you can also call the parse method directly, but be sure to wrap it in a try..catch try { type = omni::net::protocol_type::parse("INET"); } catch (const std::exception& ex) { std::cout << "Error parsing: " << ex.what() << std::endl; } std::cout << "Current protocol type: " << type << std::endl; // will print the default value if (!omni::net::protocol_type::try_parse("IGMP", type)) { // this won't print since IGMP _is_ a valid protocol_type std::cout << "IGMP is not a valid protocol type" << std::endl; } std::cout << "Current protocol type: " << type << std::endl; // will print IGMP // You can cast the enum wrapper classes to an int type int32_t val = static_cast<int32_t>(type); // You can test equality as well if (type == val) { std::cout << "Equal" << std::endl; } else { std::cout << "Not Equal" << std::endl; } type = omni::net::protocol_type::IP; if (type > val) { std::cout << "Greater than" << std::endl; } else { std::cout << "Less than" << std::endl; } if (type < val) { std::cout << "Less than" << std::endl; } else { std::cout << "Greater than" << std::endl; } // You can also cast the object to a string, or call the to_string method std::string strtype1 = type.to_string(); std::string strtype2 = static_cast<std::string>(type); std::cout << strtype1 << " == " << strtype2 << " == " << type << std::endl;
OMNI_ENUM macro'sIt's possible to use a
template and other functions of the C++ language to create a basic enum class wrapper that one could inherit from, but creating your own derived class from that has it's own problems in both memory and object complexity as well as code readability.To over come this, the
OMNI_ENUM family of macro's can be used to create an enum type of your own in an easy and efficient way. You have a few options for creating an enum type depending on what you would like your code to do.To define a simple
enum with default-incremented values, you can use the OMNI_ENUM_DEFINE macro. To define an enum with values that are assigned (e.g. enum { VALUE = 100 };), you can use the OMNI_ENUM_ASSIGNED macro. Both of these macro's must be followed by the OMNI_ENUM_END macro to signify the definition is done. If you use either of these macros, you must use the OMNI_ENUM_DEFAULT macro to define a default value for the wrapper. It can come can after the OMNI_ENUM macro but before the OMNI_ENUM_END macro. If you have elected to use the OMNI_ENUM_ASSIGNED macro to create an enum and you would like access to the parser functions, you must use the OMNI_ENUM_ASSIGNED_PARSERS macro after the OMNI_ENUM_ASSIGNED definition.The
OMNI_ENUM_DEFINE and OMNI_ENUM_ASSIGNED macro's take the same parameters. The first parameter is the name of the enum class, and the second parameter is a variadic macro parameter that can take up to 200 parameters; those parameters are the values you wish to define within your enum wrapper.For a simple example, take the following code to create a couple of
enum wrapper types with default values.// create an enum type named "my_enum", // with values "VALUE_10, VALUE_20, NONE" set to "10, 20, 0" OMNI_ENUM_ASSIGNED(my_enum, VALUE_10=10, VALUE_20=20, NONE=0) // you must declare a default value OMNI_ENUM_DEFAULT(VALUE_20) // If using the OMNI_ENUM_ASSIGNED macro, you must add the following macro // for parsing of the values; this macro takes _ONLY_ the parameter // names and not their assigned values. OMNI_ENUM_ASSIGNED_PARSERS(VALUE_10, VALUE_20, NONE) OMNI_ENUM_END // end definition // create an enum type named "other_enum", // with values "X, Y, Z" set to "0, 1, 2" (default enum increment) OMNI_ENUM_DEFINE(other_enum, X, Y, Z) OMNI_ENUM_DEFAULT(Y) // enum default OMNI_ENUM_END // end definition
OMNI_ENUM_DEFINE macro, you do not need to include the OMNI_ENUM_ASSIGNED_PARSERS for the parsing functionality as it is included by default in the macro definition.A few additional notes about these macros; if you are using the
OMNI_ENUM_ASSIGNED macro, you do not need to actually define the values, so you could do something like this: OMNI_ENUM_ASSIGNED(my_enum, VALUE_1, VALUE_2, NONE) and they will be default incremented. As well, any values you have defined in the OMNI_ENUM_ASSIGNED macro should be additionally named in the OMNI_ENUM_ASSIGNED_PARSERS macro (like above), however, if there are values you do not wish to have the enum wrapper include as valid parsable values, simply do not include that value in the macro, for example:OMNI_ENUM_ASSIGNED(my_enum, VALUE_1, VALUE_2, NONE) OMNI_ENUM_DEFAULT(VALUE_2) OMNI_ENUM_ASSIGNED_PARSERS(VALUE_1, VALUE_2) OMNI_ENUM_END
enum wrapper named my_enum and will have 3 values in it that will be default incremented and a default value of VALUE_2 however, since the NONE value was not included in the OMNI_ENUM_ASSIGNED_PARSERS if you were to try and call my_enum::parse("NONE") it would error as that was not included in the list of parsable values.The last macro that can be utilized to create an
enum wrapper class is the more simplistic OMNI_ENUM macro which can only be used to create default incremented wrappers. It takes 3 parameters, the first is the enum wrapper name, the second parameter is the default value of the wrapper, and the last parameter is like the OMNI_ENUM_DEFINE and OMNI_ENUM_ASSIGNED macros, that is, it is a variadic macro parameter that can take up to 200 parameters that will be the values of the enum wrapper.Here is a simple example to create an
enum wrapper class using the OMNI_ENUM macro:OMNI_ENUM(other_enum, // name Y, // default value X, Y, Z) // parameters // Note that you do _NOT_ need the OMNI_ENUM_END macro to end the // definition and, if used, will cause compilation errors!
OMNI_ENUM macro is a simplistic macro that can be used to create simple enum wrappers in 1 line of code.For more information on each of the macro's and how to utilize them, you can click on any of them throughout this example.
Example
#include <omnilib> OMNI_ENUM(easy_enum, NONE, VALUE_1, VALUE_2, VALUE_3, VALUE_4, NONE) OMNI_ENUM_ASSIGNED(my_enum, VALUE_10 = 10, VALUE_20 = 20, NONE = 0) OMNI_ENUM_DEFAULT(VALUE_20) OMNI_ENUM_ASSIGNED_PARSERS(VALUE_10, VALUE_20) OMNI_ENUM_END // end definition OMNI_ENUM_DEFINE(other_enum, X, Y, Z) OMNI_ENUM_DEFAULT(Y) OMNI_ENUM_END // end definition class Foo { public: OMNI_ENUM(InnerEnum, J, A, B, C, D, E, F, G, H, I, J, K, NONE) InnerEnum val; Foo() : val(InnerEnum::B) { } }; template < typename ENUM > void print(const ENUM& val, const std::string& good_parse, const std::string& bad_parse) { ENUM tval; std::cout << "enum = " << val << std::endl << "value = " << val.value() << std::endl << "to_string = " << val.to_string() << std::endl << "std::string = " << static_cast<std::string>(val) << std::endl << "uint32_t = " << static_cast<uint32_t>(val) << std::endl << "val.count = " << val.count() << std::endl << "ENUM::COUNT() = " << ENUM::COUNT() << std::endl << "ENUM::DEFAULT_VALUE() = " << ENUM::DEFAULT_VALUE() << std::endl << "ENUM::try_parse(" << good_parse << ") = " << (ENUM::try_parse(good_parse, tval) ? "good" : "bad") << std::endl << "ENUM::try_parse(" << bad_parse << ") = " << (ENUM::try_parse(bad_parse, tval) ? "good" : "bad") << std::endl << std::endl; } int main(int argc, char* argv[]) { Foo foo; Foo::InnerEnum fe; easy_enum ee = easy_enum::VALUE_3; my_enum me = my_enum::VALUE_20; other_enum oe; print(foo.val, "A", "Z"); print(fe, "NONE", "L"); print(ee, "VALUE_1", "VALUE_5"); print(me, "VALUE_20", "NONE"); print(oe, "Z", "A"); return 0; }