From 79b3bd2fd07f05b00dd6e5ccc9075e79fee8adc5 Mon Sep 17 00:00:00 2001 From: Charles Cabergs Date: Thu, 16 Oct 2025 19:32:29 +0000 Subject: [PATCH 1/3] Add duration template for all function using std::chrono::milliseconds --- tests/poller.cpp | 2 ++ tests/timers.cpp | 12 +++++++++- zmq.hpp | 62 +++++++++++++++++++++++++++++++----------------- zmq_addon.hpp | 6 +++-- 4 files changed, 57 insertions(+), 25 deletions(-) diff --git a/tests/poller.cpp b/tests/poller.cpp index 174270b3..0dffb61c 100644 --- a/tests/poller.cpp +++ b/tests/poller.cpp @@ -131,6 +131,8 @@ TEST_CASE("poller wait with no handlers throws", "[poller]") /// \todo the actual error code should be checked CHECK_THROWS_AS(poller.wait_all(events, std::chrono::milliseconds{10}), zmq::error_t); + CHECK_THROWS_AS(poller.wait_all(events, std::chrono::microseconds{10000}), + zmq::error_t); } #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 3) diff --git a/tests/timers.cpp b/tests/timers.cpp index 4229fd5a..3cef2735 100644 --- a/tests/timers.cpp +++ b/tests/timers.cpp @@ -24,10 +24,15 @@ TEST_CASE("timers add/execute", "[timers]") bool handler_ran = false; timers.add(4ms, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran); CHECK(timers.timeout().has_value()); + // Check any std::chrono duration support + std::chrono::microseconds _ = timers.timeout().value(); CHECK(!handler_ran); std::this_thread::sleep_for(10ms); timers.execute(); CHECK(handler_ran); + // Check any std::chrono duration support + auto id2 = + timers.add(std::chrono::seconds{4}, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran); } TEST_CASE("timers add/cancel", "[timers]") @@ -59,6 +64,12 @@ TEST_CASE("timers set_interval", "[timers]") std::this_thread::sleep_for(10ms); timers.execute(); CHECK(handler_ran); + handler_ran = false; + // Check any std::chrono duration support + timers.set_interval(id, std::chrono::microseconds{1000}); + std::this_thread::sleep_for(3ms); + timers.execute(); + CHECK(handler_ran); } TEST_CASE("timers reset", "[timers]") @@ -74,7 +85,6 @@ TEST_CASE("timers reset", "[timers]") timers.reset(id); CHECK(timers.timeout().has_value()); CHECK(!handler_ran); - } #endif // defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS) diff --git a/zmq.hpp b/zmq.hpp index ad0509e8..a1582f26 100644 --- a/zmq.hpp +++ b/zmq.hpp @@ -351,16 +351,19 @@ inline int poll(std::vector const &items, long timeout_ = -1) return detail::poll(const_cast(items.data()), items.size(), timeout_); } -inline int -poll(zmq_pollitem_t *items, size_t nitems, std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}) +template +int +poll(zmq_pollitem_t *items, size_t nitems, Duration timeout = std::chrono::milliseconds{-1}) { - return detail::poll(items, nitems, static_cast(timeout.count())); + auto timeout_ms = std::chrono::duration_cast(timeout); + return detail::poll(items, nitems, static_cast(timeout_ms.count())); } -inline int poll(std::vector &items, - std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}) +template +int poll(std::vector &items, Duration timeout = std::chrono::milliseconds{-1}) { - return detail::poll(items.data(), items.size(), static_cast(timeout.count())); + auto timeout_ms = std::chrono::duration_cast(timeout); + return detail::poll(items.data(), items.size(), static_cast(timeout_ms.count())); } ZMQ_DEPRECATED("from 4.3.1, use poll taking std::chrono::duration instead of long") @@ -369,11 +372,11 @@ inline int poll(std::vector &items, long timeout_) return detail::poll(items.data(), items.size(), timeout_); } -template -inline int poll(std::array &items, - std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}) +template +int poll(std::array &items, Duration timeout = std::chrono::milliseconds{-1}) { - return detail::poll(items.data(), items.size(), static_cast(timeout.count())); + auto timeout_ms = std::chrono::duration_cast(timeout); + return detail::poll(items.data(), items.size(), static_cast(timeout_ms.count())); } #endif @@ -2366,6 +2369,14 @@ class monitor_t on_monitor_started(); } +#ifdef ZMQ_CPP11 + template + bool check_event(Duration timeout = std::chrono::milliseconds{0}) + { + return check_event(std::chrono::duration_cast(timeout).count()); + } +#endif + bool check_event(int timeout = 0) { assert(_monitor_socket); @@ -2725,9 +2736,9 @@ template class poller_t } } - template + template size_t wait_all(Sequence &poller_events, - const std::chrono::milliseconds timeout) + const Duration timeout) { static_assert(std::is_same::value, "Sequence::value_type must be of poller_t::event_type"); @@ -2735,7 +2746,7 @@ template class poller_t poller_ptr.get(), reinterpret_cast(poller_events.data()), static_cast(poller_events.size()), - static_cast(timeout.count())); + static_cast(std::chrono::duration_cast(timeout).count())); if (rc > 0) return static_cast(rc); @@ -2804,9 +2815,11 @@ class timers using fn_t = zmq_timer_fn; #if CPPZMQ_HAS_OPTIONAL - using timeout_result_t = std::optional; + template + using timeout_result_t = std::optional; #else - using timeout_result_t = detail::trivial_optional; + template + using timeout_result_t = detail::trivial_optional; #endif timers() : _timers(zmq_timers_new()) @@ -2824,9 +2837,11 @@ class timers ZMQ_ASSERT(rc == 0); } - id_t add(std::chrono::milliseconds interval, zmq_timer_fn handler, void *arg) + template + id_t add(Duration interval, zmq_timer_fn handler, void *arg) { - id_t timer_id = zmq_timers_add(_timers, interval.count(), handler, arg); + auto interval_ms = std::chrono::duration_cast(interval); + id_t timer_id = zmq_timers_add(_timers, interval_ms.count(), handler, arg); if (timer_id == -1) throw zmq::error_t(); return timer_id; @@ -2839,9 +2854,11 @@ class timers throw zmq::error_t(); } - void set_interval(id_t timer_id, std::chrono::milliseconds interval) + template + void set_interval(id_t timer_id, Duration interval) { - int rc = zmq_timers_set_interval(_timers, timer_id, interval.count()); + auto interval_ms = std::chrono::duration_cast(interval); + int rc = zmq_timers_set_interval(_timers, timer_id, interval_ms.count()); if (rc == -1) throw zmq::error_t(); } @@ -2853,12 +2870,13 @@ class timers throw zmq::error_t(); } - timeout_result_t timeout() const + template + timeout_result_t timeout() const { int timeout = zmq_timers_timeout(_timers); if (timeout == -1) - return timeout_result_t{}; - return std::chrono::milliseconds{timeout}; + return timeout_result_t{}; + return std::chrono::duration_cast(std::chrono::milliseconds{timeout}); } void execute() diff --git a/zmq_addon.hpp b/zmq_addon.hpp index c6b4462c..f7142c72 100644 --- a/zmq_addon.hpp +++ b/zmq_addon.hpp @@ -805,7 +805,8 @@ class active_poller_t base_poller.modify(fd, events); } - size_t wait(std::chrono::milliseconds timeout) + template + size_t wait(Duration timeout) { if (need_rebuild) { poller_events.resize(handlers.size()); @@ -816,7 +817,8 @@ class active_poller_t } need_rebuild = false; } - const auto count = base_poller.wait_all(poller_events, timeout); + auto timeout_ms = std::chrono::duration_cast(timeout); + const auto count = base_poller.wait_all(poller_events, timeout_ms); std::for_each(poller_events.begin(), poller_events.begin() + static_cast(count), [](decltype(base_poller)::event_type &event) { From ce6c01d724b0af4f4e7a8d1ee76262990990c096 Mon Sep 17 00:00:00 2001 From: Charles Cabergs Date: Sun, 19 Oct 2025 16:29:06 +0200 Subject: [PATCH 2/3] Fix timers timeout_result_t to stay as milliseconds duration only since the template would be a breaking change in C++17 and bellow --- zmq.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/zmq.hpp b/zmq.hpp index a1582f26..4e29db92 100644 --- a/zmq.hpp +++ b/zmq.hpp @@ -2815,11 +2815,9 @@ class timers using fn_t = zmq_timer_fn; #if CPPZMQ_HAS_OPTIONAL - template - using timeout_result_t = std::optional; + using timeout_result_t = std::optional; #else - template - using timeout_result_t = detail::trivial_optional; + using timeout_result_t = detail::trivial_optional; #endif timers() : _timers(zmq_timers_new()) @@ -2871,11 +2869,11 @@ class timers } template - timeout_result_t timeout() const + timeout_result_t timeout() const { int timeout = zmq_timers_timeout(_timers); if (timeout == -1) - return timeout_result_t{}; + return timeout_result_t{}; return std::chrono::duration_cast(std::chrono::milliseconds{timeout}); } From d3e16f1b935db7582fd1aa2ffbd962ecf4302108 Mon Sep 17 00:00:00 2001 From: Charles Cabergs Date: Sun, 19 Oct 2025 16:40:10 +0200 Subject: [PATCH 3/3] Fix monitor_t::check_event with std::chrono durations --- tests/monitor.cpp | 18 ++++++++++++++++++ zmq.hpp | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/monitor.cpp b/tests/monitor.cpp index 5eb0dbd1..1e7c8a9e 100644 --- a/tests/monitor.cpp +++ b/tests/monitor.cpp @@ -85,6 +85,24 @@ TEST_CASE("monitor init event count", "[monitor]") CHECK(monitor.total == expected_event_count); } +#if __cplusplus >= 202002L +static_assert(requires { + [](){ + mock_monitor_t monitor; + monitor.check_event(); + monitor.check_event(0); + monitor.check_event(100); + monitor.check_event(std::chrono::milliseconds{0}); + monitor.check_event(std::chrono::milliseconds{100}); + monitor.check_event(std::chrono::microseconds{100}); + monitor.check_event(std::chrono::nanoseconds{100}); + monitor.check_event(std::chrono::seconds{100}); + monitor.check_event(std::chrono::minutes{100}); + monitor.check_event(std::chrono::hours{100}); + }(); +}); +#endif + TEST_CASE("monitor init abort", "[monitor]") { class mock_monitor : public mock_monitor_t diff --git a/zmq.hpp b/zmq.hpp index 4e29db92..cfcba610 100644 --- a/zmq.hpp +++ b/zmq.hpp @@ -2373,7 +2373,7 @@ class monitor_t template bool check_event(Duration timeout = std::chrono::milliseconds{0}) { - return check_event(std::chrono::duration_cast(timeout).count()); + return check_event(static_cast(std::chrono::duration_cast(timeout).count())); } #endif