Jan 232015
 

Sean Parent posted today via Twitter this small neat algorithm:

 

 

Problem:

During my today’s work I came across the problem that inside a predicate there was a check if a specific enum value is equal to a set of possible values.

So the code looked like this:

I don’t like this collection of comparisons for equalness. From my point of view it is some kind of semantically copy-and-paste.

So with Sean’s code in mind I started thinking and I searched for alternatives. The first used a std::initializer_list:

So the example from above would read like this:

That is already better, but it does not read so fluently. So I tried to solve this with a builder like pattern:

The code example would then be like this:

That’s much better, but the curly braces inside the parameter are annoying. It would read much better this way and would be less typing, don’t you think?

So this is my next attempt to solve it with variadic templates:

My next idea was, that there must be a way to do it with variadic templates without the recursion terminating bool in(T)  method.

 

So I played around a bit with Sean’s code, after I understood what it is actually doing, this is the result (thanks to cpplearner for the improvement hint):

One disadvantage of this solution is, that the order of evaluation of the arguments is not defined.

But with Jay Miller’s comment, based on Eric Niebler’s improved version of the for_each_arg, this keeps the order of evaluation of the arguments.

Conclusion:

The last version is the most compact one, but it has the disadvantage compared to all the others, that the internal comparisons does not terminate early. So the code will compare all possible values, even a match was already found. As long as the value to be checked against the set of values cannot be evaluated during compile time, I don’t see a way to do it in a similar way like the last version. If you see one, please let me know.

Unfortunately the code does not fulfill Sean’s challenge: “Goal for Better Code: Fit each function in a tweet”. Never the less I think it is worth sharing it.

Since this is almost my very first experiment with variadic templates, all ideas for improvements or comments are welcome.

 

Note:

Please use “crayon” to enter source code in he comment. The “code” tag removes all “<” and “>”.

This code does not compile with Visual Studio 2013 update 3 in release mode, only in debug. It works fine with update 4 and VS2015 CTP and the recent Clang and GCC.

 

  8 Responses to “A Small Set Algorithm for Enum Values”

  1. Avatar

    Nice, this is my old C++03 solution to the same problem
    Syntax:

     namespace _impl {
      template 
      struct equal_any_impl {
        equal_any_impl(const ValueType& ref) : ref_(ref), status_(false) {}
        equal_any_impl& operator<<(const ValueType& test) {
          if (test == ref_)
            status_ = true;
          return *this;
        }
        operator bool() const {
          return status_;
        }
      private:
        bool status_;
        const ValueType& ref_;
      };
    }
    template 
    _impl::equal_any_impl equal_any(const ValueType& ref) {
      return _impl::equal_any_impl(ref);
    }
    
    
    			
  2. Avatar

    Based on Eric Niebler’s follow up to this same tweet, this version guarantees order of evaluation:

    • Felix Petriconi

      Yea. I wrote this before the final version of Eric was available. Thanks for the hint. I will update it.

  3. Avatar

    For that matter, in the original Sean Parent post, the reason for the comma operation in the pack expansion is because the values being unpacked are function calls that may return void. Yours has a real value so that comma is unnecessary:

  4. Avatar

    The &r is not necessary: the lambda expression, namely [&r](...){}, doesn’t need to know what r is. Only the computation of its arguments uses the variable r.

    • Felix Petriconi

      You are right. I oversaw this. Will correct it.
      Many thanks!

  5. Calling any_of provides early termination.

Sorry, the comment form is closed at this time.