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 {