Skip to content

Wise enum extension #22

Open
Open

Description

@koczwak

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.

Activity

  1. quicknir commented on Apr 4, 2019

    @quicknir
    Owner

    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.

  2. koczwak commented on Apr 4, 2019

    @koczwak
    Author
    1. Yes, exactly. It's returning optional value.
    2. We use << for debugging puposes only, and to_string to get textual representation
    3. 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).
  3. madmongo1 commented on May 18, 2019

    @madmongo1
    Contributor

    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;                                                                                                                 \
        }
    
    
  4. quicknir commented on May 18, 2019

    @quicknir
    Owner

    @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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions