c++c++17dryenum-class

DRY: C++11 enum class vs C enum


I have class like this:

struct InsertResult{
    enum class Status{
        INSERTED            ,
        UPDATED_IN_PLACE    ,
        REPLACED            ,
        SKIP_INSERTED       ,
        ERROR_NO_MEMORY     ,
        ERROR_INVALID       ,
        ERROR
    };

    bool        ok;
    Status      status;
    // more members here...

    // spill the enum:
    constexpr static auto INSERTED          =  Status::INSERTED         ;
    constexpr static auto UPDATED_IN_PLACE  =  Status::UPDATED_IN_PLACE ;
    constexpr static auto REPLACED          =  Status::REPLACED         ;
    constexpr static auto SKIP_INSERTED     =  Status::SKIP_INSERTED    ;
    constexpr static auto ERROR_NO_MEMORY   =  Status::ERROR_NO_MEMORY  ;
    constexpr static auto ERROR_INVALID     =  Status::ERROR_INVALID    ;
    constexpr static auto ERROR             =  Status::ERROR            ;
}

the idea of constexpr static members is to avoid double typing, e.g.

if (insert() == InsertResult::INSERTED){
//  OK...
}

vs

if (insert() == InsertResult::Status::INSERTED){
//  OK...
}

Any easy way to make it "better" without using C enum?


Solution

  • In C++20 you would do using enum Status;.

    Pre-C++20 the best idea I have is writing a macro to generate your constexpr variables for you. The example below uses macro_sequence_for, a little library I made to simplify preprocessor loops.

    run on gcc.godbolt.org

    #include <macro_sequence_for.h>
    
    #define MAKE_ENUM(name_, members_) \
        enum class name_ {SF_FOR_EACH(BODY_ENUM_ELEM, SF_NULL, SF_NULL,, members_)};\
        SF_FOR_EACH(BODY_VAR, SF_STATE, SF_NULL, name_, members_)
    
    #define BODY_ENUM_ELEM(n, d, x) x,
    #define BODY_VAR(n, d, x) [[maybe_unused]] static constexpr d x = d::x;
    

    Then this:

    MAKE_ENUM( E,
        (e1)
        (e2)
        (e3)
    )
    

    Expands to this:

    enum class E
    {
        e1,
        e2,
        e3,
    };
    
    [[maybe_unused]] static constexpr E e1 = E::e1;
    [[maybe_unused]] static constexpr E e2 = E::e2;
    [[maybe_unused]] static constexpr E e3 = E::e3;
    

    And here's a longer version that lets you specify the enum constant values:

    #include <macro_sequence_for.h>
    
    #define MAKE_ENUM(name_, members_) \
        enum class name_ {SF_FOR_EACH(BODY_ENUM_ELEM, SF_NULL, SF_NULL,, members_)};\
        SF_FOR_EACH(BODY_VAR, SF_STATE, SF_NULL, name_, members_)
    
    #define BODY_ENUM_ELEM(n, d, ...) BODY_ENUM_ELEM_SELECT(__VA_ARGS__, BODY_ENUM_ELEM_A, BODY_ENUM_ELEM_B,)(__VA_ARGS__)
    #define BODY_ENUM_ELEM_SELECT(x, y, z, ...) z
    #define BODY_ENUM_ELEM_A(name_, init_) name_ = init_,
    #define BODY_ENUM_ELEM_B(name_) name_,
    
    #define BODY_VAR(n, d, ...) BODY_VAR_(n, d, __VA_ARGS__,)
    #define BODY_VAR_(n, d, x, ...) [[maybe_unused]] static constexpr d x = d::x;
    
    MAKE_ENUM( E,
        (e1)
        (e2, 42)
        (e3)
    )