Fire & Forget wxTimers Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
jpo234
Experienced Solver
Experienced Solver
Posts: 70
Joined: Tue Feb 25, 2020 11:34 am

Fire & Forget wxTimers

Post by jpo234 »

Hello all,
with boost::asio I usually do something like this:

Code: Select all

            namespace ba = boost::asio;
            auto timer = std::make_shared<ba::steady_timer>(m_ioctxt);
            timer->expires_after(std::chrono::milliseconds(500));
            timer->async_wait([this, timer, somedata](const boost::system::error_code &) { handle_timeout(somedata); });
How do people deal with an undefined number of wxTimers with varying duration? Do I have to do a manual cleanup (urgh)?

Thanks
Jorg
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Fire & Forget wxTimers

Post by doublemax »

Nothing stops you from using boost in a wxWidgets app. You just need to make sure the handler code is executed from the main thread if it accesses GUI elements.
Use the source, Luke!
jpo234
Experienced Solver
Experienced Solver
Posts: 70
Joined: Tue Feb 25, 2020 11:34 am

Re: Fire & Forget wxTimers

Post by jpo234 »

doublemax wrote: Mon May 31, 2021 4:19 pm Nothing stops you from using boost in a wxWidgets app. You just need to make sure the handler code is executed from the main thread if it accesses GUI elements.
That's the fallback plan. But I'm now really curious: Is there a clean way to deal with a varying number of wxTimers? The idea I was just investigating was to use wxTimerEvent::GetTimer() and then call Detroy() on the wxTimer, but apparently there is no wxTimer::Detroy() method.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Fire & Forget wxTimers

Post by doublemax »

You can just delete it. I'm not sure if you can delete a timer from inside a timer event handler, if not use wxTheApp->ScheduleForDestruction()
https://docs.wxwidgets.org/trunk/classw ... 22f9de9d90
Use the source, Luke!
jpo234
Experienced Solver
Experienced Solver
Posts: 70
Joined: Tue Feb 25, 2020 11:34 am

Re: Fire & Forget wxTimers

Post by jpo234 »

doublemax wrote: Mon May 31, 2021 4:30 pm You can just delete it. I'm not sure if you can delete a timer from inside a timer event handler, if not use wxTheApp->ScheduleForDestruction()
https://docs.wxwidgets.org/trunk/classw ... 22f9de9d90
This seems to work fine. Can I ask for a short code review?

Code: Select all

#pragma once
#include <wx/wx.h>

#include <chrono>
#include <functional>

class SimpleTimer : wxTimer {
  public:
    static void create(const std::chrono::system_clock::duration &timeout, const std::function<void()> &func) { new SimpleTimer(timeout, func); }

  private:
    SimpleTimer(const std::chrono::system_clock::duration &timeout, const std::function<void()> &func) {
        Bind(wxEVT_TIMER, [this, func](wxTimerEvent event) {
            func();
            wxTheApp->ScheduleForDestruction(this);
        });
        SetOwner(this);
        {
            using namespace std::chrono;
            StartOnce(duration_cast<milliseconds>(timeout).count());
        }
    }
};

Code: Select all

                SimpleTimer::create(std::chrono::milliseconds(1000), []() {
                // do something here
                });
OK?
Last edited by jpo234 on Wed Jun 02, 2021 8:33 am, edited 1 time in total.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Fire & Forget wxTimers

Post by doublemax »

Looks fine to me. The SetOwner() call is probably not needed, as you're binding the event handler to the timer anyway.
Use the source, Luke!
jpo234
Experienced Solver
Experienced Solver
Posts: 70
Joined: Tue Feb 25, 2020 11:34 am

Re: Fire & Forget wxTimers

Post by jpo234 »

doublemax wrote: Wed Jun 02, 2021 8:32 am Looks fine to me. The SetOwner() call is probably not needed, as you're binding the event handler to the timer anyway.
Thanks. I just made the constructor private to guarantee that the timer is created via new. Otherwise wxTheApp->ScheduleForDestruction() might be called with a static or stack instance which is probably bad.
jpo234
Experienced Solver
Experienced Solver
Posts: 70
Joined: Tue Feb 25, 2020 11:34 am

Re: Fire & Forget wxTimers

Post by jpo234 »

Final version after some more tinkering:

Code: Select all

class SimpleTimer : wxTimer {
  public:
    template <typename Rep, typename Period> static void create(const std::chrono::duration<Rep, Period> &timeout, const std::function<void()> &func) {
        auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count();
        wxApp::GetInstance()->CallAfter([milliseconds, func] { new SimpleTimer(milliseconds, func); });
    }

  private:
    SimpleTimer(int milliseconds, const std::function<void()> &func) {
        Bind(wxEVT_TIMER, [this, func](wxTimerEvent) {
            func();
            wxTheApp->ScheduleForDestruction(this);
        });
        StartOnce(milliseconds);
    }
};
AmadeusK525
Experienced Solver
Experienced Solver
Posts: 61
Joined: Wed Aug 19, 2020 12:04 am

Re: Fire & Forget wxTimers

Post by AmadeusK525 »

Hello,
Will the timers be all calling the same function? If so, an easy way to handle this is to use and event table macro to bind the function to the event and just setting the same ID for all timers you need. Then, if you have 100 timers but all with the same ID, they'll all call the same function when they fire. But I see you've already figured out another way to handle it.

Oh, btw, you should consider passing the events by reference in your event handlers ('wxTimerEvent& event' instead of 'wxTimerEvent event'). Don't know if you did it on purpose but I don't see why you would copy the event every time the function gets called.

Thanks
jpo234
Experienced Solver
Experienced Solver
Posts: 70
Joined: Tue Feb 25, 2020 11:34 am

Re: Fire & Forget wxTimers

Post by jpo234 »

AmadeusK525 wrote: Wed Jun 02, 2021 12:53 pm Hello,
Will the timers be all calling the same function? If so, an easy way to handle this is to use and event table macro to bind the function to the event and just setting the same ID for all timers you need. Then, if you have 100 timers but all with the same ID, they'll all call the same function when they fire. But I see you've already figured out another way to handle it.

Oh, btw, you should consider passing the events by reference in your event handlers ('wxTimerEvent& event' instead of 'wxTimerEvent event'). Don't know if you did it on purpose but I don't see why you would copy the event every time the function gets called.

Thanks
I'm happy with what I have. I can now do:

Code: Select all

            using namespace std::chrono_literals;
            utils::SimpleTimer::create(500ms, [] { Say("Hello World"); });
No fuzz, no worries, which is exactly what I wanted.
jpo234
Experienced Solver
Experienced Solver
Posts: 70
Joined: Tue Feb 25, 2020 11:34 am

Re: Fire & Forget wxTimers

Post by jpo234 »

AmadeusK525 wrote: Wed Jun 02, 2021 12:53 pm Oh, btw, you should consider passing the events by reference in your event handlers ('wxTimerEvent& event' instead of 'wxTimerEvent event'). Don't know if you did it on purpose but I don't see why you would copy the event every time the function gets called.
Well spotted! I changed it accordingly,
Post Reply