C++ expand variadic template arguments into a statement
Asked Answered
D

3

5

I'm currently playing around with template metaprogramming. I'm trying to make a finite state machine by using tmp. I know that there are several implementations in the web but I want to implement one by myself as an exercise.

I have a class called Condition which is the base class for the condition of a transition between two states. One implementation is the AnyCondition class:

template<class Input, Input comp, Input ... comps >
class AnyCondition: public Condition<Input>
{    
public:
    AnyCondition() {}

    bool operator()(const Input& input) const override
    {
        return input == comp || AnyCondition<Input, comps...>()(input);
    }

};

The thing here is, that the compiler will expand this recursivly, which results in a lot of recursive calls at runtime due to the input parameter. It should be more efficent, if the expanded code would be a statement like:

    bool operator()(const Input& input) const override
    {
        return input == comp1 || input == comp2 || input == comp3...
    }

Is this possible somehow?

Davita answered 12/1, 2017 at 21:37 Comment(6)
You could use va_list to use loop through them and check each comp value?Macur
I can't test it right now, but are you sure the compiler still uses recursive calls when compiled with optimisation on?Shelia
I don't think that the compiler will expand this recursively, but that it will expand that at compile time into a code as you desire. Have you tested that?Titus
@Macur va_list is not template metaprogramming. The OP is looking for a compile time solution, with type safety.Yarrow
Ah, okay thanks! PaulMacur
@Titus Yeah that was what I meant. I actually didn't test how the compiler expands the code. That was just my interpretation of what I know about this topic so far.Davita
C
7

C++17 solution - fold expression:

template <typename... Ts>
auto anyCondition(Ts... xs)
{
    return (xs || ...);
}

wandbox example


C++11 solution - for_each_argument:

template <typename TF, typename... Ts>
void for_each_argument(TF&& f, Ts&&... xs)
{
    (void)(int[]){(f(std::forward<Ts>(xs)), 0)...};
}

template <typename... Ts>
auto anyCondition(Ts... xs)
{
    bool acc = false;
    for_each_argument([&acc](bool x){ acc = acc || x; }, xs...);
    return acc;
}   

wandbox example

I gave a talk about this snippet at CppCon 2015:
CppCon 2015: Vittorio Romeo “for_each_argument explained and expanded"

Chintzy answered 12/1, 2017 at 21:58 Comment(1)
Thanks! And a great talk! Unforunatly VS 2017 RC doesn't support fold expressions right now so I had to use the C++11 solution. I had to make a little change to the for_each_argument function because your version isn't supportet by C++ compiler (according to error C4576). I replaced it with the initializer list that you suggested in your talk: std::initializer_list<int>{(f(std::forward<Ts>(xs)), 0)... };.Davita
F
2

I am pretty much sure any decent compiler will optimize recursion into loop. But, if you are looking for some extra ways to expand variadic argument list of a single type, you can use simple std::initializer_list trick:

constexpr auto list = {comp, comps...};

Or, in your case:

inline bool operator()(const Input &input) const override {
    bool res = false;
    for (auto val : {comp, comps...})
        res |= val == input;
    return res;
}
Flittermouse answered 13/1, 2017 at 8:58 Comment(0)
T
1

There is the good old comma trick

bool operator()(const Input& input) const override
{
  bool ret { input == comp };

  int  unusedA [] { ( ret |= (input == comps), 0 ) ... };

  (void)unusedA; // to avoid the unused warning

  return ret;
}
Tabbatha answered 12/1, 2017 at 21:51 Comment(1)
Care to expand on the comma trick? Is it just to introduce a sequence point?Yarrow

© 2022 - 2024 — McMap. All rights reserved.