0

I'm trying to create a Finite State Machine hierarchy type structure. What I'm trying to do is check to see if the current state exist if it doesn't return, then check to see if all of the next states exist. As soon as one of them fails it will return as well.

I'm not sure if it can be done using fold expressions or variadic parameter pack expansion or not, but I've been getting errors for parameter pack not being expanded. I'm not sure if I can do it this way or if I would need a helper function or some other mechanism.

Here is my approach:

template<unsigned N>
class FSM {
public:
    std::vector<State<N>> states;

    // ... other parts of class

    template<typename Current, typename... NextStates>
    void addStateTransition(Current* currentState, NextStates*... nextStates) {
        // look to see if the current state is a state in our container.
        auto current = std::find(states.begin(), states.end(), currentState);
        if (current == states_.end()) {
            std::cout << "Could not find " << currentState->id_ << " in this State Machine.";
            return;
        }

        // Able to use fold expressions or not to check if all of the next states are in our container?
        auto next = std::find(states.begin(), states.end(), nextStates); 
        // ? I've tried the ellipsis inside, outside and on both sides of the ending parenthesis, and none of them work. 
        if (next == states.end()) {
            std::cout << "Could not find " << nextStates->id_ << " in this State Machine.";
            return;
        }

        // if all of nextStates... are found, do something else here
    }
};
Francis Cugler
  • 7,788
  • 2
  • 28
  • 59

1 Answers1

1

To use a fold-expression, you need something you can fold over. You need some expression for each element in the parameter pack. The expression you need is complicated: a call to std::find, checking the result, etc. So it's best to stick that in a lambda:

auto lookup = [&](auto nextState) {
    // one single find
    auto it = std::find(states.begin(), states.end(), nextState); 
    if (it == states.end()) {
        std::cout << "Could not find " << nextState->id_ << " in this State Machine.";
        return false;
    }        
    return true;
};

// fold over that
bool const allFound = (lookup(nextStates) && ...);

allFound will be true if all the states are found, or false if at least one is missing... in which case something will be logged. This handles empty packs too... if nextStates... is empty, allFound is trivially true.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • I'll have to check that out immediately! – Francis Cugler Aug 20 '19 at 14:09
  • Okay I made some progress with that but now I'm getting a C2678: compiler error - `no operator found which takes a left-hand operand of type 'State<2>' (or there is no acceptable conversion)`... – Francis Cugler Aug 20 '19 at 14:18
  • I had to take your approach and do something similar within my `State` class and now it works like a charm. However; in order for my `FSM` class to use my `State` class I did have to define an `operator==()` within my `State` class. Your feed back was very useful and helpful in solving several of my problems. You have also given me the ability to realize a new set of tools! I now know how to use `fold expressions` over a `lambda` for `parameter pack` expansions! – Francis Cugler Aug 20 '19 at 18:37