A couple of years ago I asked a question using an example to simulate a rich Java like enum class in C++0x. I am asking for suggestions on how to improve the design with a goal towards (1) machieving constexpr and (2) avoiding the manual steps where I effectively (a) declare the nested enum values, (b) declare and hopefully soon constexpr initialize the static fields, and (c) initialize the set of strict weak ordered enums. I wonder if templates may help in this area.
The code is pretty straight forward and is shown in the following coliru live demo.
Specifically, I wanted to be able to:
- Switch on values.
The code snippet below shows how this works, the restriction was that instead of switching on the BetterEnum::EnumValue1, I had to instead switch on the nested enum value - this is not obvious but it uses the integral cast operator:
//! Integral cast operator for switch statements (cast to named enum).
constexpr operator const Value() const noexcept {
return mValue;
}
I am not sure how to work around this restriction.
BetterEnum val = BetterEnum::EnumValue1;
switch (val) {
case BetterEnum::enumvalue1:
std::cout << BetterEnum::EnumValue1.getStringVal() << std::endl;
break;
case BetterEnum::enumvalue2:
std::cout << BetterEnum::EnumValue2.getStringVal() << std::endl;
break;
case BetterEnum::enumvalue3:
std::cout << BetterEnum::EnumValue3.getStringVal() << std::endl;
break;
default:
;
}
std::cout << BetterEnum::EnumValue1 << std::endl;
- Perform string based enum lookup.
//! Lookup Enum by <code>aStringVal</code>.
static BetterEnum valueOf(const std::string_view aStringVal) {
for (const auto& rNext : getValues()) {
if (rNext.getStringVal() == aStringVal) {
return rNext;
}
}
throw std::invalid_argument(
std::string("Illegal Argument: ") + std::string(aStringVal));
}
- Perform value based enum lookup.
//! Lookup Enum by <code>aValue</code>.
static BetterEnum valueOf(const Value aValue) {
for (const auto& next : getValues()) {
if (next.mValue == aValue) {
return next;
}
}
throw std::invalid_argument(
std::string("Illegal Argument: ") + std::to_string(aValue));
}
- Implement as header only.
I recently added support for this by using inline const initializers at the bottom of the class.
inline const BetterEnum BetterEnum::EnumValue1(BetterEnum::enumvalue1, "EnumValue1"sv);
inline const BetterEnum BetterEnum::EnumValue2(BetterEnum::enumvalue2, "EnumValue2"sv);
inline const BetterEnum BetterEnum::EnumValue3(BetterEnum::enumvalue3, "EnumValue3"sv);
- Add constexpr support.
Although partially added - use of the std::set<T> restricts the number of members that I can make constexpr as this would require std::set to be constexpr (which it is not, although std::array<T, N> is but that doesn't guarantee the uniqueness of the emum entries ordered via strict weak ordering via the operator<() operator).
My original non-constexpr design used strings - I had to change these to string_views to the constexpr constructor - however I am not sure how to avoid the using namespace std::literals; in the header file to avoid polluting the namespace for every file that includes this header.
- Add stream insertion operator support.
friend std::ostream& operator<<(std::ostream& os, const BetterEnum& rhs) {
os << rhs.getStringVal();
return os;
}
We have moved on a few years and are currently at c++17 with C++20 looming on the horizon. I wanted to see if I could improve on the the class which I have been dragging along on various projects over the years.
Specifically I would like to know if there is some way to make the static members constexpr. C++17 introduced the notion of improved static variable initialization. This allowed me to improve the class to a header only implementation, however if I attempt to initialize the static class members inside the class definition
class BetterEnum final {
public:
enum Value {
enumvalue1, enumvalue2, enumvalue3
};
static constexpr BetterEnum EnumValue1 = BetterEnum(BetterEnum::enumvalue1, "EnumValue1"sv);
static constexpr BetterEnum EnumValue2 = BetterEnum(BetterEnum::enumvalue2, "EnumValue2"sv);
static constexpr BetterEnum EnumValue3 = BetterEnum(BetterEnum::enumvalue3, "EnumValue3"sv);
...
}
I get the following errors:
main.cpp:15:95: error: invalid use of incomplete type 'class BetterEnum'
static constexpr BetterEnum EnumValue1 = BetterEnum(BetterEnum::enumvalue1, "EnumValue1"sv);
^
main.cpp:9:7: note: definition of 'class BetterEnum' is not complete until the closing brace
class BetterEnum final {
Previously only methods/functions could be specified as inline, but now you can do the same with variablesIs that what you want to emulate? Some other feature? The only way to emulate it 100% is to use C++.