summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2016-09-06 14:18:50 +0200
committerUlf Hermann <ulf.hermann@qt.io>2016-09-12 15:37:50 +0000
commit314931009b6205e58391afcff6b25ee317dd7d91 (patch)
treedab8279e3afdbe119ef8bc5e65802de862b54bbc
parenta1ca15291fec29194348dccf213b0587a505aafb (diff)
Add onEntry and onExit templates to QScxmlStateMachines
These help to avoid repeating "if (isInState) ..." if you only want to execute a handler when a state is entered, or "if (!isInState) ..." if you want to execute it only when a state is left. Change-Id: I0a73585591163dd2741b2d9d4897ee50e9623853 Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
-rw-r--r--src/scxml/qscxmlstatemachine.cpp96
-rw-r--r--src/scxml/qscxmlstatemachine.h78
-rw-r--r--tests/auto/compiled/compiled.pro2
-rw-r--r--tests/auto/compiled/tst_compiled.cpp30
-rw-r--r--tests/auto/statemachine/statemachine.pro2
-rw-r--r--tests/auto/statemachine/tst_statemachine.cpp98
6 files changed, 303 insertions, 3 deletions
diff --git a/src/scxml/qscxmlstatemachine.cpp b/src/scxml/qscxmlstatemachine.cpp
index 326cb80..915844c 100644
--- a/src/scxml/qscxmlstatemachine.cpp
+++ b/src/scxml/qscxmlstatemachine.cpp
@@ -177,6 +177,102 @@ Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
Returns a handle to the connection, which can be used later to disconnect.
*/
+/*!
+ \fn auto QScxmlStateMachine::onEntry(const QObject *receiver,
+ const char *method)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a method on \a receiver using QMetaObject::invokeMethod() if that argument
+ is \c true and \a receiver has not been deleted, yet.
+
+ The given \a method must not accept any arguments. \a method is the plain
+ method name, not enclosed in \c SIGNAL() or \c SLOT().
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is entered.
+
+ onEntry() is only available if the compiler supports return type
+ deduction for functions.
+*/
+
+/*!
+ \fn auto QScxmlStateMachine::onExit(const QObject *receiver,
+ const char *method)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a method on \a receiver using QMetaObject::invokeMethod() if that argument
+ is \c false and \a receiver has not been deleted, yet.
+
+ The given \a method must not accept any arguments. \a method is the plain
+ method name, not enclosed in SIGNAL(...) or SLOT(...).
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is left.
+
+ onExit() is only available if the compiler supports return type
+ deduction for functions.
+*/
+
+/*!
+ \fn QScxmlStateMachine::onEntry(Functor functor)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a functor if that argument is \c true. The given \a functor must not
+ accept any arguments.
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is entered.
+
+ onEntry() is only available if the compiler supports return type
+ deduction for functions.
+ */
+
+/*!
+ \fn QScxmlStateMachine::onExit(Functor functor)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a functor if that argument is \c false. The given \a functor must not
+ accept any arguments.
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is left.
+
+ onExit() is only available if the compiler supports return type
+ deduction for functions.
+ */
+
+/*!
+ \fn QScxmlStateMachine::onEntry(const QObject *receiver,
+ PointerToMemberFunction method)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a method on \a receiver if that argument is \c true and the \a receiver
+ has not been deleted, yet. The given \a method must not accept any
+ arguments.
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is entered.
+
+ onEntry() is only available if the compiler supports return type
+ deduction for functions.
+ */
+
+/*!
+ \fn QScxmlStateMachine::onExit(const QObject *receiver,
+ PointerToMemberFunction method)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a method on \a receiver if that argument is \c false and the \a receiver
+ has not been deleted, yet. The given \a method must not accept any
+ arguments.
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is left.
+
+ onExit() is only available if the compiler supports return type
+ deduction for functions.
+ */
+
namespace QScxmlInternal {
static int signalIndex(const QMetaObject *meta, const QByteArray &signalName)
diff --git a/src/scxml/qscxmlstatemachine.h b/src/scxml/qscxmlstatemachine.h
index 787a7c6..e5a0b92 100644
--- a/src/scxml/qscxmlstatemachine.h
+++ b/src/scxml/qscxmlstatemachine.h
@@ -50,6 +50,7 @@
#include <QVector>
#include <QUrl>
#include <QVariantList>
+#include <QPointer>
QT_BEGIN_NAMESPACE
class QIODevice;
@@ -169,6 +170,83 @@ public:
}
#endif
+#ifdef Q_QDOC
+ static auto onEntry(const QObject *receiver, const char *method);
+ static auto onExit(const QObject *receiver, const char *method);
+
+ template<typename Functor>
+ static auto onEntry(Functor functor);
+
+ template<typename Functor>
+ static auto onExit(Functor functor);
+
+ template<typename PointerToMemberFunction>
+ static auto onEntry(const QObject *receiver, PointerToMemberFunction method);
+
+ template<typename PointerToMemberFunction>
+ static auto onExit(const QObject *receiver, PointerToMemberFunction method);
+#elif defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+ static auto onEntry(const QObject *receiver, const char *method)
+ {
+ const QPointer<QObject> receiverPointer(const_cast<QObject *>(receiver));
+ return [receiverPointer, method](bool isEnteringState) {
+ if (isEnteringState && !receiverPointer.isNull())
+ QMetaObject::invokeMethod(const_cast<QObject *>(receiverPointer.data()), method);
+ };
+ }
+
+ static auto onExit(const QObject *receiver, const char *method)
+ {
+ const QPointer<QObject> receiverPointer(const_cast<QObject *>(receiver));
+ return [receiverPointer, method](bool isEnteringState) {
+ if (!isEnteringState && !receiverPointer.isNull())
+ QMetaObject::invokeMethod(receiverPointer.data(), method);
+ };
+ }
+
+ template<typename Functor>
+ static auto onEntry(Functor functor)
+ {
+ return [functor](bool isEnteringState) {
+ if (isEnteringState)
+ functor();
+ };
+ }
+
+ template<typename Functor>
+ static auto onExit(Functor functor)
+ {
+ return [functor](bool isEnteringState) {
+ if (!isEnteringState)
+ functor();
+ };
+ }
+
+ template<typename Func1>
+ static auto onEntry(const typename QtPrivate::FunctionPointer<Func1>::Object *receiver,
+ Func1 slot)
+ {
+ typedef typename QtPrivate::FunctionPointer<Func1>::Object Object;
+ const QPointer<Object> receiverPointer(const_cast<Object *>(receiver));
+ return [receiverPointer, slot](bool isEnteringState) {
+ if (isEnteringState && !receiverPointer.isNull())
+ (receiverPointer->*slot)();
+ };
+ }
+
+ template<typename Func1>
+ static auto onExit(const typename QtPrivate::FunctionPointer<Func1>::Object *receiver,
+ Func1 slot)
+ {
+ typedef typename QtPrivate::FunctionPointer<Func1>::Object Object;
+ const QPointer<Object> receiverPointer(const_cast<Object *>(receiver));
+ return [receiverPointer, slot](bool isEnteringState) {
+ if (!isEnteringState && !receiverPointer.isNull())
+ (receiverPointer->*slot)();
+ };
+ }
+#endif // defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+
QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec,
const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection);
diff --git a/tests/auto/compiled/compiled.pro b/tests/auto/compiled/compiled.pro
index 713f484..c13cbf4 100644
--- a/tests/auto/compiled/compiled.pro
+++ b/tests/auto/compiled/compiled.pro
@@ -1,5 +1,5 @@
QT = core gui qml testlib scxml
-CONFIG += testcase
+CONFIG += testcase c++14
TARGET = tst_compiled
CONFIG += console
diff --git a/tests/auto/compiled/tst_compiled.cpp b/tests/auto/compiled/tst_compiled.cpp
index 0e4bb02..e09300e 100644
--- a/tests/auto/compiled/tst_compiled.cpp
+++ b/tests/auto/compiled/tst_compiled.cpp
@@ -135,8 +135,21 @@ public slots:
{
received = received || enabled;
}
+
+ void enter()
+ {
+ entered = true;
+ }
+
+ void exit()
+ {
+ exited = true;
+ }
+
public:
bool received = false;
+ bool entered = false;
+ bool exited = false;
};
void tst_Compiled::connection()
@@ -155,6 +168,16 @@ void tst_Compiled::connection()
QMetaObject::Connection conB = stateMachine.connectToState("b", &receiverB, SLOT(receive(bool)));
QMetaObject::Connection conFinal = stateMachine.connectToState("final", &receiverFinal, SLOT(receive(bool)));
+#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+ // C++14 available: test for onEntry and onExit
+ typedef QScxmlStateMachine QXSM;
+ QMetaObject::Connection aEntry = stateMachine.connectToState("a", QXSM::onEntry(&receiverA, "enter"));
+ QMetaObject::Connection aExit = stateMachine.connectToState("a", QXSM::onExit(&receiverA, "exit"));
+
+ QVERIFY(aEntry);
+ QVERIFY(aExit);
+#endif
+
QVERIFY(conA);
QVERIFY(conA1);
QVERIFY(conA2);
@@ -174,6 +197,13 @@ void tst_Compiled::connection()
QVERIFY(disconnect(conA2));
QVERIFY(disconnect(conB));
QVERIFY(disconnect(conFinal));
+
+#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+ QVERIFY(receiverA.entered);
+ QVERIFY(!receiverA.exited);
+ QVERIFY(disconnect(aEntry));
+ QVERIFY(disconnect(aExit));
+#endif
}
class MyConnection : public Connection
diff --git a/tests/auto/statemachine/statemachine.pro b/tests/auto/statemachine/statemachine.pro
index 0e4de1a..95bf9a8 100644
--- a/tests/auto/statemachine/statemachine.pro
+++ b/tests/auto/statemachine/statemachine.pro
@@ -1,5 +1,5 @@
QT = core gui qml testlib scxml-private
-CONFIG += testcase
+CONFIG += testcase c++14
TARGET = tst_statemachine
CONFIG += console
diff --git a/tests/auto/statemachine/tst_statemachine.cpp b/tests/auto/statemachine/tst_statemachine.cpp
index 227d427..1cf0518 100644
--- a/tests/auto/statemachine/tst_statemachine.cpp
+++ b/tests/auto/statemachine/tst_statemachine.cpp
@@ -47,6 +47,7 @@ private Q_SLOTS:
void activeStateNames_data();
void activeStateNames();
void connections();
+ void onExit();
void eventOccurred();
void doneDotStateEvent();
@@ -141,10 +142,21 @@ public slots:
bReached = bReached || enabled;
}
+ void aEnter()
+ {
+ aEntered = true;
+ }
+
+ void aExit()
+ {
+ aExited = true;
+ }
+
public:
bool aReached = false;
bool bReached = false;
-
+ bool aEntered = false;
+ bool aExited = false;
};
void tst_StateMachine::connections()
@@ -171,6 +183,44 @@ void tst_StateMachine::connections()
});
QVERIFY(final);
+#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+ // C++14 available, test onEntry and onExit
+ bool a1Entered = false;
+ bool a1Exited = false;
+ bool finalEntered = false;
+ bool finalExited = false;
+ typedef QScxmlStateMachine QXSM;
+
+ QMetaObject::Connection aEntry = stateMachine->connectToState(
+ "a", QXSM::onEntry(&receiver, &Receiver::aEnter));
+ QVERIFY(aEntry);
+ QMetaObject::Connection aExit = stateMachine->connectToState(
+ "a", QXSM::onExit(&receiver, &Receiver::aExit));
+ QVERIFY(aExit);
+ QMetaObject::Connection a1Entry = stateMachine->connectToState("a1", &receiver,
+ QXSM::onEntry([&a1Entered]() {
+ a1Entered = true;
+ }));
+ QVERIFY(a1Entry);
+ QMetaObject::Connection a1Exit = stateMachine->connectToState("a1", &receiver,
+ QXSM::onExit([&a1Exited]() {
+ a1Exited = true;
+ }));
+ QVERIFY(a1Exit);
+
+ QMetaObject::Connection finalEntry = stateMachine->connectToState(
+ "final", QXSM::onEntry([&finalEntered]() {
+ finalEntered = true;
+ }));
+ QVERIFY(finalEntry);
+
+ QMetaObject::Connection finalExit = stateMachine->connectToState(
+ "final", QXSM::onExit([&finalExited]() {
+ finalExited = true;
+ }));
+ QVERIFY(finalExit);
+#endif
+
stateMachine->start();
QTRY_VERIFY(a1Reached);
@@ -182,6 +232,52 @@ void tst_StateMachine::connections()
QVERIFY(disconnect(b));
QVERIFY(disconnect(a1));
QVERIFY(disconnect(final));
+
+#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+ QVERIFY(receiver.aEntered);
+ QVERIFY(!receiver.aExited);
+ QVERIFY(a1Entered);
+ QVERIFY(!a1Exited);
+ QVERIFY(finalEntered);
+ QVERIFY(!finalExited);
+
+ QVERIFY(disconnect(aEntry));
+ QVERIFY(disconnect(aExit));
+ QVERIFY(disconnect(a1Entry));
+ QVERIFY(disconnect(a1Exit));
+ QVERIFY(disconnect(finalEntry));
+ QVERIFY(disconnect(finalExit));
+#endif
+}
+
+void tst_StateMachine::onExit()
+{
+#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+ // Test onExit being actually called
+
+ typedef QScxmlStateMachine QXSM;
+ QScopedPointer<QXSM> stateMachine(QXSM::fromFile(QString(":/tst_statemachine/eventoccurred.scxml")));
+
+ Receiver receiver;
+ bool aExited1 = false;
+
+ stateMachine->connectToState("a", QXSM::onExit([&aExited1]() { aExited1 = true; }));
+ stateMachine->connectToState("a", QXSM::onExit(&receiver, &Receiver::aExit));
+ stateMachine->connectToState("a", QXSM::onExit(&receiver, "aEnter"));
+ {
+ // Should not crash
+ Receiver receiver2;
+ stateMachine->connectToState("a", QXSM::onEntry(&receiver2, &Receiver::aEnter));
+ stateMachine->connectToState("a", QXSM::onEntry(&receiver2, "aExit"));
+ stateMachine->connectToState("a", QXSM::onExit(&receiver2, &Receiver::aExit));
+ stateMachine->connectToState("a", QXSM::onExit(&receiver2, "aEnter"));
+ }
+
+ stateMachine->start();
+ QTRY_VERIFY(receiver.aEntered);
+ QTRY_VERIFY(receiver.aExited);
+ QTRY_VERIFY(aExited1);
+#endif
}
bool hasChildEventRouters(QScxmlStateMachine *stateMachine)