QTimer: add support for <chrono> functions

[ChangeLog][QtCore][QTimer] Added support for std::chrono duration
objects for QTimer methods, like QTimer::singleShot and
QTimer::setInterval.

Change-Id: I87e17314d8b24ae983b1fffd14536e24d5b12424
Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
bb10
Thiago Macieira 2016-05-30 16:10:08 -03:00
parent b2c0f9713c
commit d16ba63f5c
3 changed files with 245 additions and 18 deletions

View File

@ -532,6 +532,78 @@ void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiv
\sa start()
*/
/*!
\fn void QTimer::singleShot(std::chrono::duration<Rep, Period> value, const QObject *receiver, const char *member)
\since 5.8
\overload
\reentrant
This static function calls a slot after a given time interval.
It is very convenient to use this function because you do not need
to bother with a \l{QObject::timerEvent()}{timerEvent} or
create a local QTimer object.
The \a receiver is the receiving object and the \a member is the slot. The
time interval is given in the duration object \a value.
\sa start()
*/
/*!
\fn void QTimer::singleShot(std::chrono::duration<Rep, Period> value, Qt::TimerType timerType, const QObject *receiver, const char *member)
\since 5.8
\overload
\reentrant
This static function calls a slot after a given time interval.
It is very convenient to use this function because you do not need
to bother with a \l{QObject::timerEvent()}{timerEvent} or
create a local QTimer object.
The \a receiver is the receiving object and the \a member is the slot. The
time interval is given in the duration object \a value. The \a timerType affects the
accuracy of the timer.
\sa start()
*/
/*!
\fn void QTimer::start(std::chrono::duration<Rep, Period> value)
\since 5.8
\overload
Starts or restarts the timer with a timeout of duration \a value.
If the timer is already running, it will be
\l{QTimer::stop()}{stopped} and restarted.
If \l singleShot is true, the timer will be activated only once.
*/
/*!
\fn std::chrono::milliseconds QTimer::intervalAsDuration() const
\since 5.8
Returns the interval of this timer as a \c std::chrono::milliseconds object.
\sa interval
*/
/*!
\fn std::chrono::milliseconds QTimer::remainingTimeAsDuration() const
\since 5.8
Returns the time remaining in this timer object as a \c
std::chrono::milliseconds object. If this timer is due or overdue, the
returned value is \c std::chrono::milliseconds::zero(). If the remaining
time could not be found or the timer is not active, this function returns a
negative duration.
\sa remainingTime()
*/
/*!
\property QTimer::singleShot
\brief whether the timer is a single-shot timer

View File

@ -47,6 +47,10 @@
#include <QtCore/qbasictimer.h> // conceptual inheritance
#include <QtCore/qobject.h>
#if QT_HAS_INCLUDE(<chrono>)
# include <chrono>
#endif
QT_BEGIN_NAMESPACE
@ -94,13 +98,13 @@ public:
static void singleShot(int msec, Qt::TimerType timerType, const QObject *context, Functor functor);
#else
// singleShot to a QObject slot
template <typename Func1>
static inline void singleShot(int msec, const typename QtPrivate::FunctionPointer<Func1>::Object *receiver, Func1 slot)
template <typename Duration, typename Func1>
static inline void singleShot(Duration interval, const typename QtPrivate::FunctionPointer<Func1>::Object *receiver, Func1 slot)
{
singleShot(msec, msec >= 2000 ? Qt::CoarseTimer : Qt::PreciseTimer, receiver, slot);
singleShot(interval, defaultTypeFor(interval), receiver, slot);
}
template <typename Func1>
static inline void singleShot(int msec, Qt::TimerType timerType, const typename QtPrivate::FunctionPointer<Func1>::Object *receiver,
template <typename Duration, typename Func1>
static inline void singleShot(Duration interval, Qt::TimerType timerType, const typename QtPrivate::FunctionPointer<Func1>::Object *receiver,
Func1 slot)
{
typedef QtPrivate::FunctionPointer<Func1> SlotType;
@ -109,42 +113,42 @@ public:
Q_STATIC_ASSERT_X(int(SlotType::ArgumentCount) == 0,
"The slot must not have any arguments.");
singleShotImpl(msec, timerType, receiver,
singleShotImpl(interval, timerType, receiver,
new QtPrivate::QSlotObject<Func1, typename SlotType::Arguments, void>(slot));
}
// singleShot to a functor or function pointer (without context)
template <typename Func1>
template <typename Duration, typename Func1>
static inline typename QtPrivate::QEnableIf<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
!QtPrivate::is_same<const char*, Func1>::value, void>::Type
singleShot(int msec, Func1 slot)
singleShot(Duration interval, Func1 slot)
{
singleShot(msec, msec >= 2000 ? Qt::CoarseTimer : Qt::PreciseTimer, Q_NULLPTR, slot);
singleShot(interval, defaultTypeFor(interval), nullptr, slot);
}
template <typename Func1>
template <typename Duration, typename Func1>
static inline typename QtPrivate::QEnableIf<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
!QtPrivate::is_same<const char*, Func1>::value, void>::Type
singleShot(int msec, Qt::TimerType timerType, Func1 slot)
singleShot(Duration interval, Qt::TimerType timerType, Func1 slot)
{
singleShot(msec, timerType, Q_NULLPTR, slot);
singleShot(interval, timerType, nullptr, slot);
}
// singleShot to a functor or function pointer (with context)
template <typename Func1>
template <typename Duration, typename Func1>
static inline typename QtPrivate::QEnableIf<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
!QtPrivate::is_same<const char*, Func1>::value, void>::Type
singleShot(int msec, QObject *context, Func1 slot)
singleShot(Duration interval, QObject *context, Func1 slot)
{
singleShot(msec, msec >= 2000 ? Qt::CoarseTimer : Qt::PreciseTimer, context, slot);
singleShot(interval, defaultTypeFor(interval), context, slot);
}
template <typename Func1>
template <typename Duration, typename Func1>
static inline typename QtPrivate::QEnableIf<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
!QtPrivate::is_same<const char*, Func1>::value, void>::Type
singleShot(int msec, Qt::TimerType timerType, QObject *context, Func1 slot)
singleShot(Duration interval, Qt::TimerType timerType, QObject *context, Func1 slot)
{
//compilation error if the slot has arguments.
typedef QtPrivate::FunctionPointer<Func1> SlotType;
Q_STATIC_ASSERT_X(int(SlotType::ArgumentCount) <= 0, "The slot must not have any arguments.");
singleShotImpl(msec, timerType, context,
singleShotImpl(interval, timerType, context,
new QtPrivate::QFunctorSlotObject<Func1, 0,
typename QtPrivate::List_Left<void, 0>::Value, void>(slot));
}
@ -159,6 +163,43 @@ public Q_SLOTS:
Q_SIGNALS:
void timeout(QPrivateSignal);
public:
#if QT_HAS_INCLUDE(<chrono>) || defined(Q_QDOC)
template <class Rep, class Period>
void setInterval(std::chrono::duration<Rep, Period> value)
{
setInterval(std::chrono::duration_cast<std::chrono::milliseconds>(value).count());
}
std::chrono::milliseconds intervalAsDuration() const
{
return std::chrono::milliseconds(interval());
}
std::chrono::milliseconds remainingTimeAsDuration() const
{
return std::chrono::milliseconds(remainingTime());
}
template <class Rep, class Period>
static void singleShot(std::chrono::duration<Rep, Period> value, const QObject *receiver, const char *member)
{
singleShot(int(std::chrono::duration_cast<std::chrono::milliseconds>(value).count()), receiver, member);
}
template <class Rep, class Period>
static void singleShot(std::chrono::duration<Rep, Period> value, Qt::TimerType timerType, const QObject *receiver, const char *member)
{
singleShot(int(std::chrono::duration_cast<std::chrono::milliseconds>(value).count()), timerType, receiver, member);
}
template <class Rep, class Period>
void start(std::chrono::duration<Rep, Period> value)
{
start(int(std::chrono::duration_cast<std::chrono::milliseconds>(value).count()));
}
#endif
protected:
void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
@ -168,9 +209,25 @@ private:
inline int startTimer(int){ return -1;}
inline void killTimer(int){}
static Q_DECL_CONSTEXPR Qt::TimerType defaultTypeFor(int msecs) Q_DECL_NOTHROW
{ return msecs >= 2000 ? Qt::CoarseTimer : Qt::PreciseTimer; }
static void singleShotImpl(int msec, Qt::TimerType timerType,
const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj);
#if QT_HAS_INCLUDE(<chrono>)
template <class Rep, class Period>
static Qt::TimerType defaultTypeFor(std::chrono::duration<Rep, Period> interval)
{ return defaultTypeFor(int(std::chrono::duration_cast<std::chrono::milliseconds>(interval).count())); }
template <class Rep, class Period>
static void singleShotImpl(std::chrono::duration<Rep, Period> interval, Qt::TimerType timerType,
const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj)
{
singleShotImpl(int(std::chrono::duration_cast<std::chrono::milliseconds>(interval).count()),
timerType, receiver, slotObj);
}
#endif
int id, inter, del;
uint single : 1;
uint nulltimer : 1;

View File

@ -52,6 +52,7 @@ private slots:
void remainingTime();
void remainingTimeDuringActivation_data();
void remainingTimeDuringActivation();
void basic_chrono();
void livelock_data();
void livelock();
void timerInfiniteRecursion_data();
@ -68,6 +69,7 @@ private slots:
void singleShotStaticFunctionZeroTimeout();
void recurseOnTimeoutAndStopTimer();
void singleShotToFunctors();
void singleShot_chrono();
void crossThreadSingleShotToFunctor();
void dontBlockEvents();
@ -214,6 +216,57 @@ void tst_QTimer::remainingTimeDuringActivation()
}
}
void tst_QTimer::basic_chrono()
{
#if !QT_HAS_INCLUDE(<chrono>)
QSKIP("This test requires C++11 <chrono> support");
#else
// duplicates zeroTimer, singleShotTimeout, interval and remainingTime
using namespace std::chrono;
TimerHelper helper;
QTimer timer;
timer.setInterval(nanoseconds(0));
timer.start();
QCOMPARE(timer.intervalAsDuration().count(), milliseconds::rep(0));
QCOMPARE(timer.remainingTimeAsDuration().count(), milliseconds::rep(0));
connect(&timer, SIGNAL(timeout()), &helper, SLOT(timeout()));
QCoreApplication::processEvents();
QCOMPARE(helper.count, 1);
helper.count = 0;
timer.start(milliseconds(100));
QCOMPARE(helper.count, 0);
QTest::qWait(TIMEOUT_TIMEOUT);
QVERIFY(helper.count > 0);
int oldCount = helper.count;
QTest::qWait(TIMEOUT_TIMEOUT);
QVERIFY(helper.count > oldCount);
helper.count = 0;
timer.start(microseconds(200000));
QCOMPARE(timer.intervalAsDuration().count(), milliseconds::rep(200));
QTest::qWait(50);
QCOMPARE(helper.count, 0);
milliseconds rt = timer.remainingTimeAsDuration();
QVERIFY2(qAbs(rt.count() - 150) < 50, qPrintable(QString::number(rt.count())));
helper.count = 0;
timer.setSingleShot(true);
timer.start(milliseconds(100));
QTest::qWait(500);
QCOMPARE(helper.count, 1);
QTest::qWait(500);
QCOMPARE(helper.count, 1);
helper.count = 0;
#endif
}
void tst_QTimer::livelock_data()
{
QTest::addColumn<int>("interval");
@ -787,6 +840,51 @@ void tst_QTimer::singleShotToFunctors()
_t = Q_NULLPTR;
}
void tst_QTimer::singleShot_chrono()
{
#if !QT_HAS_INCLUDE(<chrono>)
QSKIP("This test requires C++11 <chrono> support");
#else
// duplicates singleShotStaticFunctionZeroTimeout and singleShotToFunctors
using namespace std::chrono;
TimerHelper helper;
QTimer::singleShot(hours(0), &helper, SLOT(timeout()));
QTest::qWait(500);
QCOMPARE(helper.count, 1);
QTest::qWait(500);
QCOMPARE(helper.count, 1);
TimerHelper nhelper;
QTimer::singleShot(seconds(0), &nhelper, &TimerHelper::timeout);
QCoreApplication::processEvents();
QCOMPARE(nhelper.count, 1);
QCoreApplication::processEvents();
QCOMPARE(nhelper.count, 1);
int count = 0;
QTimer::singleShot(microseconds(0), CountedStruct(&count));
QCoreApplication::processEvents();
QCOMPARE(count, 1);
_e.reset(new QEventLoop);
QTimer::singleShot(0, &StaticEventLoop::quitEventLoop);
QCOMPARE(_e->exec(), 0);
QObject c3;
QTimer::singleShot(milliseconds(500), &c3, CountedStruct(&count));
QTest::qWait(800);
QCOMPARE(count, 2);
QTimer::singleShot(0, [&count] { ++count; });
QCoreApplication::processEvents();
QCOMPARE(count, 3);
_e.reset();
#endif
}
class DontBlockEvents : public QObject
{
Q_OBJECT