Mar 262015
 

I just finished the first draft of the implementation of my enum_extend library. It is available via github.

It offers

  • Ranges over all defined enumerated values
  • Filtered ranges over a subset of enumerated values
  • ++ and — pre- and postfix operators
  • Attaching a textual representation to each enumerated value

Some examples:

Only this specialization and the implementation of the operators is necessary:

There exists a set of macros that simplifies the process of specializing the extender and implementing the operators.

Much more details and examples can be found in this first tutorial

Feedback is welcome!

 

  3 Responses to “enum_extend: C++ library that extends the functionality of enum types”

  1. Hi Felix,

    You asked for feedback so here I go 🙂

    I really like the way you think outside of the box but I do have some objections to your enum extender.
    Please let me elaborate a bit in detail so we can have a constructive discussion about it.

    1.) The ++ and — operator for enums.

    It seems that you think that the ‘missing’ increment and decrement operator for enums are an oversight and should be part of the standard. I like to disagree.

    Basically an enum is a compile time constant value and beeing able to incrementing/decrementing a constant value kind of defeats its purpose.
    IMHO this is just as bad as abusing an enum as a bitset but I will come back to that later.

    Unlike other languages C++ is extremly fexible when it comes to the definition of enum values. They do not need to be contiguous, they can have any arbitary ording (or none at all) and they even allow duplicate values.

    I am not saying that this actually makes sense but this is valid C++ and it is not that unlikly that you will find something like this in legacy code. As a matter of fact, I did a couple of times.

    So now consider the following code using your enum extender:

    So the following restrictions need to be meet in order for the enum extender to work properly:
    – The enum definitions must be ordered.
    – No duplicates allowed.
    – Since the operators work on the index rather on the actual enum value it is dangerous to add/remove enum values in between existing ones.

    None of this is really enforceable so there is a high risk that the code using the extender will silently break over time.

    Aside from the fact that I do not like the idea to alter an constant in any way or form, especially the last restriction is a no-go as far as I am concerned.

    If you are now puzzeld what I mean with the last restriction, I am thinking along the lines of something like this:

    Five months later someone modifies the enum to this:

    So the code above may silenty break because the ‘next’ color is now Color::Blue instead of Color::Green.

    2.) The ranged filter.

    I did not test this. As I said earlier I treat enums as compile time constant values and I consider any abuse of an enum as a bitset as a design fault.
    To be quiet honest I probably did something like this in the past too but nowadays I convert them into an plain int and be happy with it.
    If it is used like an int, feels like an int and looks like an int, it mostlikly is/should be an int.

    So I do not like an utility class that supports/endorse this kind of abuse.

    3.) Iterating over all enums.

    This is where I am completely on your side and I do see the need for something like this.
    Unfortunatly I do not like the idea to duplicate the definition as your code requires it.
    This is a manual step that is bound to cause problems. Luckily I can get away in a lot of cases using this little trick:

    Like all tricks this has some strings attached to it. It works only for auto increment enum values, but will give you an compile time error as soon as you try something else so it can not be easily missused.

    I usally frown upon precompiler ‘magic’ but IMHO this is simple enough to be understood by any developer. Due to the missing name qualifing it only works for c-style enums, it may be possible to extend the precompiler ‘magic’ to solve this too, but that would require to much of such ‘magic’ for my liking.

    Please note that I used a std::set rather then a std::vector, because this only works for auto increment enum values there wont be any duplicates. Of course you can also change that to a std::vector if that suits you better.

    If you find the restrictions acceptable this may be a viable alternative to your solution. Of course you need an compiler that fully supports initializer lists.

    Best regards,
    Lutz

    • Felix Petriconi

      Hi Lutz,

      many thanks for your reply. Let me go into a detailed answer:

      1) I see your point and I should add a requirement / and add a check that no duplicated value can be used. At least if one wants to use the ++ and — operators.
      I am not on your side, that enums are just a compile time constant. That is true for each individual value. But I see an enum definition as a set or a group of constants and there are use cases, where it makes sense to iterate over all or a subset of values. There are cases were ++ must be used inside a for loop, where it is not appropriate to use a range based for loop.

      Your example that one gets incorrect results is true, if one relies on a value that was reached through and ++ operator. But in C++ are many ways to shoot into your foot. Eg take an array of values and you use in the application a reference to one of the values in the array. Now someone changes the order of elements and you would run into a similar problem if one relies on the specific value.

      The extender cannot break silently over time. Only it’s usage 😉 Changing values during runtime is not possible, because I provide on purpose only const iterators from the extender and the interface forbids after initialization changes on the underlying container.

      2) The bit operations was just an example to use a filtered range. One can put anything into the predicate that returns true for a certain subset of enums. Using this, one can abstract nicely from the underlying low level implementation.

      3) Your way of defining the enums is a nice alternative. But I do not require a duplication of the code. Forseeing this, I provide a zoo of macros that do the same, even for enum classes and enums with a specific storage specifier. I am still working on the documentation of it.

      std::set look nice on the first glance, but their memory management is from the point of view of performance bad. A contiguous vector is much better.

  2. Hi Felix,

    sorry for getting back to you so late…

    Well I would not object to a next() and previous() method. It is the self modifying aspect of the ++ and — operators that I do not like in conjunction with enum values.

    Sure the std::set comes with a price tag attached to it. The reason I picked it in this example is because it automatically removes duplicates and applies a strict weak ordering to the content.

    Currently you are using std::find_if and that will bog you down to O(n). Thats okay for small enums that will fit in one or two cachelines but as soon as the computational cost of the linear search outweights the benefit from the cache/prefetch friendlynes (i.e. larger enums) your performance will start to suffer.

    A sorted set (or vector for that matter) would allow you to use a much faster binary search O(log n) instead. For VERY large enums a std::(unordered_)map may be even better but I have not done any research here.

    So i guess for small enums the std::find_if in conjunction with a cache/prefetch friendly std::vector is perfectly fine. For medium sized enumerations a std::set (or sorted std::vector) sounds more feasible. I guess that at some point (i.e. very large enumerations with lots of lookups) a std::map (or unordered_map) may become a probable alternative.

    Of course you could offer all three variations in a policy based style approach 😉

    Best regards,
    Lutz

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

(required)

(required)