- Qt 5.5
- Qt Widgets
- Factorial States Example
- Qt 5.5.1 Reference Documentation

# Factorial States Example

The statechart for calculating the factorial looks as follows:

In other words, the state machine calculates the factorial of 6 and prints the result.

    class Factorial : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(int x READ x WRITE setX)
        Q_PROPERTY(int fac READ fac WRITE setFac)
    public:
        Factorial(QObject *parent = 0)
            : QObject(parent), m_x(-1), m_fac(1)
        {
        }

        int x() const
        {
            return m_x;
        }

        void setX(int x)
        {
            if (x == m_x)
                return;
            m_x = x;
            emit xChanged(x);
        }

        int fac() const
        {
            return m_fac;
        }

        void setFac(int fac)
        {
            m_fac = fac;
        }

    Q_SIGNALS:
        void xChanged(int value);

    private:
        int m_x;
        int m_fac;
    };

The Factorial class is used to hold the data of the computation, `x` and `fac`. It also provides a signal that's emitted whenever the value of `x` changes.

    class FactorialLoopTransition : public QSignalTransition
    {
    public:
        FactorialLoopTransition(Factorial *fact)
            : QSignalTransition(fact, SIGNAL(xChanged(int))), m_fact(fact)
        {}

        virtual bool eventTest(QEvent *e) Q_DECL_OVERRIDE
        {
            if (!QSignalTransition::eventTest(e))
                return false;
            QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
            return se->arguments().at(0).toInt() > 1;
        }

        virtual void onTransition(QEvent *e) Q_DECL_OVERRIDE
        {
            QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
            int x = se->arguments().at(0).toInt();
            int fac = m_fact->property("fac").toInt();
            m_fact->setProperty("fac",  x * fac);
            m_fact->setProperty("x",  x - 1);
        }

    private:
        Factorial *m_fact;
    };

The FactorialLoopTransition class implements the guard (`x` > 1) and calculations (`fac` = `x` * `fac`; `x` = `x` - 1) of the factorial loop.

    class FactorialDoneTransition : public QSignalTransition
    {
    public:
        FactorialDoneTransition(Factorial *fact)
            : QSignalTransition(fact, SIGNAL(xChanged(int))), m_fact(fact)
        {}

        virtual bool eventTest(QEvent *e) Q_DECL_OVERRIDE
        {
            if (!QSignalTransition::eventTest(e))
                return false;
            QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
            return se->arguments().at(0).toInt() <= 1;
        }

        virtual void onTransition(QEvent *) Q_DECL_OVERRIDE
        {
            fprintf(stdout, "%d\n", m_fact->property("fac").toInt());
        }

    private:
        Factorial *m_fact;
    };

The FactorialDoneTransition class implements the guard (`x` <= 1) that terminates the factorial computation. It also prints the final result to standard output.

    int main(int argc, char **argv)
    {
        QCoreApplication app(argc, argv);
        Factorial factorial;
        QStateMachine machine;

The application's main() function first creates the application object, a Factorial object and a state machine.

        QState *compute = new QState(&machine);
        compute->assignProperty(&factorial, "fac", 1);
        compute->assignProperty(&factorial, "x", 6);
        compute->addTransition(new FactorialLoopTransition(&factorial));

The `compute` state is created, and the initial values of `x` and `fac` are defined. A FactorialLoopTransition object is created and added to the state.

        QFinalState *done = new QFinalState(&machine);
        FactorialDoneTransition *doneTransition = new FactorialDoneTransition(&factorial);
        doneTransition->setTargetState(done);
        compute->addTransition(doneTransition);

A final state, `done`, is created, and a FactorialDoneTransition object is created with `done` as its target state. The transition is then added to the `compute` state.

        machine.setInitialState(compute);
        QObject::connect(&machine, SIGNAL(finished()), &app, SLOT(quit()));
        machine.start();

        return app.exec();
    }

The machine's initial state is set to be the `compute` state. We connect the QStateMachine::finished() signal to the QCoreApplication::quit() slot, so the application will quit when the state machine's work is done. Finally, the state machine is started, and the application's event loop is entered.

Files:

- statemachine/factorial/main.cpp
- statemachine/factorial/factorial.pro