Re: Strongly-typed non-scoped enums in C++11?

Discussion in 'C++' started by Öö Tiib, Aug 23, 2013.

  1. Öö Tiib

    Öö Tiib Guest

    On Friday, 23 August 2013 10:28:33 UTC+3, Paavo Helde wrote:
    > Background: I would like to have strongly-typed enums in order to find
    > out and fix suspicious comparisons and conversions to integers. However,
    > only one of our build machines has a compiler version which supports C++
    > 11 enums, a legacy enum is needed for other builds. For the diagnostics
    > point of view it is fine if it works only in one build, so I thought
    > doing something like:
    >
    > #ifdef HAS_C11_STRONGLY_TYPED_ENUMS
    > enum class abc {
    > #else
    > enum abc {
    > #endif
    > val1,
    > val2,
    > ....
    > };
    >
    > Alas, now all the codebase must be changed to use the names abc::val1,
    > abc::val2 in the C11 branch, and must not use the prefix in the legacy
    > branch. This seems to kill the whole benefit of this approach. Any ideas?


    The idiomatic enum usage was something like that:

    // Old code
    enum abc { abc_val1, abc_val2, abc_val3 };

    With C++11 we want that:

    // New code
    enum class abc { val1, val2, val3 };

    For transition time we may want to have that:

    // Support to legacy enumerators
    static abc const abc_val1 = abc::val1;
    static abc const abc_val2 = abc::val2;
    static abc const abc_val3 = abc::val3;

    I don't think that primitive C preprocessor can somehow construct all
    above in elegant manner. It might be easier with C++98 type safe enum
    toy:

    template<typename def, typename inner = typename def::type>
    class safe_enum : public def
    {
    typedef typename def::type type;
    inner val;
    public:
    safe_enum(type v) : val(v) {}
    inner underlying() const { return val; }
    friend bool operator == (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val == rhs.val; }
    friend bool operator != (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val != rhs.val; }
    friend bool operator < (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val < rhs.val; }
    friend bool operator <= (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val <= rhs.val; }
    friend bool operator > (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val > rhs.val; }
    friend bool operator >= (const safe_enum & lhs, const safe_enum & rhs) { return lhs.val >= rhs.val; }
    };

    Then we can have something that compiles on old compilers and looks more
    preprocessable:

    // Old type safe enum usage
    struct abc_def {
    enum type { val1, val2, val3 };
    };
    typedef safe_enum<abc_def> abc;

    // Support to legacy enumerators
    static abc const abc_val1 = abc::val1;
    static abc const abc_val2 = abc::val2;
    static abc const abc_val3 = abc::val3;

    Unfortunately that "safe thing" may cause legal problems if used like
    aggregate (that it is not) and also compilers may fail to optimize its
    usage well enough.

    Pick your poison. ;)
    Öö Tiib, Aug 23, 2013
    #1
    1. Advertising

  2. Öö Tiib wrote:

    > On Friday, 23 August 2013 10:28:33 UTC+3, Paavo Helde wrote:
    >> Background: I would like to have strongly-typed enums in order to find
    >> out and fix suspicious comparisons and conversions to integers. However,
    >> only one of our build machines has a compiler version which supports C++
    >> 11 enums, a legacy enum is needed for other builds. For the diagnostics
    >> point of view it is fine if it works only in one build, so I thought
    >> doing something like:
    >>
    >> #ifdef HAS_C11_STRONGLY_TYPED_ENUMS
    >> enum class abc {
    >> #else
    >> enum abc {
    >> #endif
    >> val1,
    >> val2,
    >> ....
    >> };
    >>
    >> Alas, now all the codebase must be changed to use the names abc::val1,
    >> abc::val2 in the C11 branch, and must not use the prefix in the legacy
    >> branch. This seems to kill the whole benefit of this approach. Any ideas?

    >
    > The idiomatic enum usage was something like that:
    >
    > // Old code
    > enum abc { abc_val1, abc_val2, abc_val3 };
    >
    > With C++11 we want that:
    >
    > // New code
    > enum class abc { val1, val2, val3 };
    >
    > For transition time we may want to have that:
    >
    > // Support to legacy enumerators
    > static abc const abc_val1 = abc::val1;
    > static abc const abc_val2 = abc::val2;
    > static abc const abc_val3 = abc::val3;
    >
    > I don't think that primitive C preprocessor can somehow construct all
    > above in elegant manner.


    With the Boost preprocessor lib it should be possible to define this for
    example like this:

    DOUBLE_PURPOSE_ENUM( abc, (val1)(val2)(val3) );

    I think that with a suitable defined DOUBLE_PURPOSE_ENUM you can get
    exactly the code you wrote above. (See the type 'sequence' in the
    library documentation.)

    Gerhard
    Gerhard Fiedler, Aug 23, 2013
    #2
    1. Advertising

  3. Öö Tiib

    Öö Tiib Guest

    On Friday, 23 August 2013 20:26:42 UTC+3, Gerhard Fiedler wrote:
    > Öö Tiib wrote:
    > > The idiomatic enum usage was something like that:
    > >
    > > // Old code
    > > enum abc { abc_val1, abc_val2, abc_val3 };
    > >
    > > With C++11 we want that:
    > >
    > > // New code
    > > enum class abc { val1, val2, val3 };
    > >
    > > For transition time we may want to have that:
    > >
    > > // Support to legacy enumerators
    > > static abc const abc_val1 = abc::val1;
    > > static abc const abc_val2 = abc::val2;
    > > static abc const abc_val3 = abc::val3;
    > >
    > > I don't think that primitive C preprocessor can somehow construct all
    > > above in elegant manner.

    >
    > With the Boost preprocessor lib it should be possible to define this for
    > example like this:
    >
    > DOUBLE_PURPOSE_ENUM( abc, (val1)(val2)(val3) );
    >
    > I think that with a suitable defined DOUBLE_PURPOSE_ENUM you can get
    > exactly the code you wrote above. (See the type 'sequence' in the
    > library documentation.)


    Haven't studied it. Would you seriously recommend it? Typically
    such things come with toys working but actual lists run into some
    insanely low limits (say 256 elements max) or cause serious slow-down
    when lists are longer than 8 elements.
    Öö Tiib, Aug 23, 2013
    #3
  4. Öö Tiib wrote:

    > On Friday, 23 August 2013 20:26:42 UTC+3, Gerhard Fiedler wrote:
    >> Öö Tiib wrote:
    >>> The idiomatic enum usage was something like that:
    >>>
    >>> // Old code
    >>> enum abc { abc_val1, abc_val2, abc_val3 };
    >>>
    >>> With C++11 we want that:
    >>>
    >>> // New code
    >>> enum class abc { val1, val2, val3 };
    >>>
    >>> For transition time we may want to have that:
    >>>
    >>> // Support to legacy enumerators
    >>> static abc const abc_val1 = abc::val1;
    >>> static abc const abc_val2 = abc::val2;
    >>> static abc const abc_val3 = abc::val3;
    >>>
    >>> I don't think that primitive C preprocessor can somehow construct all
    >>> above in elegant manner.

    >>
    >> With the Boost preprocessor lib it should be possible to define this for
    >> example like this:
    >>
    >> DOUBLE_PURPOSE_ENUM( abc, (val1)(val2)(val3) );
    >>
    >> I think that with a suitable defined DOUBLE_PURPOSE_ENUM you can get
    >> exactly the code you wrote above. (See the type 'sequence' in the
    >> library documentation.)

    >
    > Haven't studied it. Would you seriously recommend it? Typically such
    > things come with toys working but actual lists run into some insanely
    > low limits (say 256 elements max) or cause serious slow-down when
    > lists are longer than 8 elements.


    I have just recently started to use Boost Preprocessor for a few
    small-scale things and found it convenient to use. It opens up the range
    of things you can do with the preprocessor without a lot of code (of
    course not counting the code in the Boost headers :).

    For larger numbers of items (you talk about 256), I have used the
    include trick a few times: the items are defined as argument(s) to a
    macro inside a file that only contains these definitions, and the file
    is included multiple times, where each time the macro is defined
    differently. For just three items, that wouldn't make much sense, but if
    it is a longer list (and especially if multiple people may have to add
    items to the list without actually being involved with the code behind
    it), separating the list itself from the code that it generates in this
    way can be quite useful.

    In this case, for example, once you can get rid of the support for the
    legacy enumerators, you just drop the code in Adc.h that generates them.
    The list itself in AbcReg.h doesn't change.

    A typical case for this is when there's a need for an enumeration with
    text associated with each value.

    File AbcReg.h:

    ITEM( val1 )
    ITEM( val2 )
    ITEM( val3 )
    //...

    File Abc.h:

    enum class abc {
    #define ITEM( item ) item,
    #include AbcReg.h
    #undef ITEM
    }

    #define ITEM( item ) static abc const abc_##item = abc::item;
    #include AbcReg.h
    #undef ITEM


    Gerhard
    Gerhard Fiedler, Aug 24, 2013
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Narayanan Sankaranarayanan

    Custom Namespace For Strongly Typed DataSet

    Narayanan Sankaranarayanan, Dec 15, 2004, in forum: ASP .Net
    Replies:
    0
    Views:
    1,090
    Narayanan Sankaranarayanan
    Dec 15, 2004
  2. Imran

    Strongly Typed dataset

    Imran, Jan 23, 2004, in forum: ASP .Net
    Replies:
    1
    Views:
    567
    Richard A. Lowe
    Jan 23, 2004
  3. Big D

    Strongly typed datasets and XML

    Big D, Feb 17, 2004, in forum: ASP .Net
    Replies:
    1
    Views:
    449
    bruce barker
    Feb 17, 2004
  4. Qiang Li
    Replies:
    5
    Views:
    520
    Gianni Mariani
    Sep 28, 2008
  5. Victor Bazarov
    Replies:
    6
    Views:
    335
    Bo Persson
    Aug 18, 2011
Loading...

Share This Page