wise_enum
wise_enum copied to clipboard
Wise enum extension
For our internal use, we extended wise-enum, adding:
- from_number function (creation of enum similiar to from_string)
- ostream operator WISE_ENUM(Foo, (Bar, 1)) Foo::Bar -> "Bar(1)"
- abort call in case of calling from_number/from_string with invalid argument
Do you want any of those features pulled into your project? We can hide them behind defines.
from_number meaning, you pass the underlying value of the enum?
For ostream, are you saying that operator<< does something to to_string, i.e. "Bar(1)" instead of "Bar"? That seems a bit surprising that ostream wouldn't be consistent with to_string don't you think?
In both cases, why an abort call? Seems like returning an empty optional is much better; it allows for proper error handling and the user is free to call abort if the optional is empty.
- Yes, exactly. It's returning optional value.
- We use << for debugging puposes only, and to_string to get textual representation
- My appologies, I meant abort in case of to_string function with invalid argument (that is not present in enum, possibly generated by bitflag enum).
I did a similar thing. very useful.
I decided to make istream operator >> permissive in that it understands text or numbers, on the basis that with IO it's helpful to be strict when writing and permissive when reading.
namespace quoine::wrap::wise_enum
{
using namespace ::wise_enum;
template < class T, std::enable_if_t< wise_enum::is_wise_enum_v< T > > * = nullptr >
void wise_enum_from_stream(std::istream &is, T &arg)
{
auto buffer = std::string();
is >> buffer;
if (is)
{
auto o = wise_enum::from_string< T >(buffer);
if (o.has_value())
arg = *o;
else
{
using int_type = std::underlying_type_t< T >;
auto num = static_cast< int_type >(std::atoll(buffer.c_str()));
bool found = false;
for (auto e : wise_enum::range< T >)
{
if (static_cast< int_type >(e.value) == num)
{
arg = e.value;
found = true;
break;
}
}
if (not found)
is.setstate(std::ios::failbit);
}
}
}
} // namespace quoine::wrap::wise_enum
#define WISE_ENUM_ENABLE_IO(Enum) \
inline auto operator<<(std::ostream &os, Enum e)->std::ostream & { return os << ::quoine::wrap::wise_enum::to_string(e); } \
inline auto operator>>(std::istream &is, Enum &e)->std::istream & \
{ \
::quoine::wrap::wise_enum::wise_enum_from_stream(is, e); \
return is; \
}
#define WISE_ENUM_MEMBER_ENABLE_IO(Enum) \
friend auto operator<<(std::ostream &os, Enum e)->std::ostream & { return os << ::quoine::wrap::wise_enum::to_string(e); } \
friend auto operator>>(std::istream &is, Enum &e)->std::istream & \
{ \
::quoine::wrap::wise_enum::wise_enum_from_stream(is, e); \
return is; \
}
@karol-koczwara-ig @madmongo1 I kind of want to be careful here because obviously there are infinite things that could be added. My main goal is to keep a relatively small set of basis functions that you should be able to implement other things "on top of", non-intrusively (so you can use wise_enum the way you want without needing to fork it).
Right now, I think I will do from_integer with similar API to from_string. It will be implemented using another public function, verify_enum, which takes an enum and returns a bool, and simply verify that the value passed in is actually a valid value (which users might find useful for other things).
I'm not really sure about the stream operators. Using operator<< as the debug representation doesn't seem like a very standard thing to do. Different people might want different debug representations too (and not everyone even uses/likes streams), so I think maybe it's just better to let users do what they want on top of to_string. Again, as @madmongo1 's code snippet shows, it's not necessary to modify wise_enum itself to get the desired behavior.
I'm not really sure why to_string would abort for an invalid argument. Currently it returns an empty string; some people may want to do something other than abort with this. If you want to abort, then you can wrap that function with my_to_string, do the if check and abort yourself. I don't really want to have lots of #defines that switch behaviors between incompatible states.