HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
callback.hpp
1// Copyright Take Vos 2023.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../utility/utility.hpp"
8#include "thread.hpp"
9#include "../macros.hpp"
10#include <memory>
11#include <atomic>
12#include <functional>
13#include <cstddef>
14#include <mutex>
15#include <algorithm>
16
17hi_export_module(hikogui.concurrency.callback);
18
19hi_export namespace hi {
20inline namespace v1 {
21namespace detail {
22
23template<typename ResultType, typename... ArgTypes>
25public:
26 virtual ~callback_base() = default;
27
28 virtual ResultType operator()(ArgTypes... args) = 0;
29};
30
31template<typename FunctionType, typename ResultType, typename... ArgTypes>
32class callback_impl : public callback_base<ResultType, ArgTypes...> {
33public:
35 {
36#ifndef NDEBUG
37 auto const _ = std::scoped_lock(_mutex);
38 hi_assert(_thread_ids.empty());
39#endif
40 }
41
42 template<typename Func>
43 callback_impl(Func&& func) : callback_base<ResultType, ArgTypes...>(), _func(std::forward<Func>(func))
44 {
45 }
46
47 ResultType operator()(ArgTypes... args) override
48 {
49#ifndef NDEBUG
50 auto const _ = std::scoped_lock(_mutex);
51 auto const thread_id = current_thread_id();
52 hi_assert(not std::ranges::contains(_thread_ids, thread_id));
53 _thread_ids.push_back(thread_id);
54 auto const d = defer([&] {
55 std::erase(_thread_ids, thread_id);
56 });
57#endif
58
59 return _func(args...);
60 }
61
62private:
63 FunctionType _func;
64
65#ifndef NDEBUG
66 mutable std::vector<thread_id> _thread_ids;
67 mutable std::mutex _mutex;
68#endif
69};
70
71} // namespace detail
72
73template<typename T = void()>
75
76template<typename T = void()>
78
79template<typename ResultType, typename... ArgTypes>
80class callback<ResultType(ArgTypes...)>;
81
82template<typename ResultType, typename... ArgTypes>
83class weak_callback<ResultType(ArgTypes...)> {
84public:
85 using result_type = ResultType;
86 using callback_type = callback<ResultType(ArgTypes...)>;
87
88 constexpr weak_callback() noexcept = default;
89 weak_callback(weak_callback const& other) noexcept = default;
90 weak_callback(weak_callback&& other) noexcept = default;
91 weak_callback& operator=(weak_callback const& other) noexcept = default;
92 weak_callback& operator=(weak_callback&& other) noexcept = default;
93
94 weak_callback(callback_type const& other) noexcept;
95
96 void reset() noexcept
97 {
98 return _impl.reset();
99 }
100
101 [[nodiscard]] long use_count() const noexcept
102 {
103 return _impl.use_count();
104 }
105
112 [[nodiscard]] bool expired() const noexcept
113 {
114 return _impl.expired();
115 }
116
117 [[nodiscard]] callback_type lock() const noexcept;
118
119private:
120 using base_impl_type = detail::callback_base<ResultType, ArgTypes...>;
121
122 std::weak_ptr<base_impl_type> _impl;
123};
124
152template<typename ResultType, typename... ArgTypes>
153class callback<ResultType(ArgTypes...)> {
154public:
155 using result_type = ResultType;
156 using weak_callback_type = weak_callback<ResultType(ArgTypes...)>;
157
158 constexpr callback() = default;
159 callback(callback const&) = default;
160 callback& operator=(callback const&) = default;
161 callback(callback&&) = default;
162 callback& operator=(callback&&) = default;
163
164 callback(std::nullptr_t) noexcept : _impl(nullptr) {}
165
166 callback& operator=(std::nullptr_t) noexcept
167 {
168 _impl = nullptr;
169 return *this;
170 }
171
172 template<typename Func>
173 explicit callback(Func&& func) : _impl(std::make_shared<impl_type<Func>>(std::forward<Func>(func)))
174 {
175 }
176
177 void reset() noexcept
178 {
179 return _impl.reset();
180 }
181
182 [[nodiscard]] long use_count() const noexcept
183 {
184 return _impl.use_count();
185 }
186
187 [[nodiscard]] operator bool() const noexcept
188 {
189 return static_cast<bool>(_impl);
190 }
191
201 template<typename... Args>
202 decltype(auto) operator()(Args... args)
203 {
204 if (not _impl) {
206 }
207
208 // The callback function does not need to be locked as the callback
209 // object can not be destroyed at this point.
210 return (*_impl)(std::forward<Args>(args)...);
211 }
212
213private:
214 using base_impl_type = detail::callback_base<ResultType, ArgTypes...>;
215
216 template<typename FunctionType>
217 using impl_type = detail::callback_impl<FunctionType, ResultType, ArgTypes...>;
218
220
222
223 friend weak_callback<ResultType(ArgTypes...)>;
224};
225
226template<typename ResultType, typename... ArgTypes>
227inline weak_callback<ResultType(ArgTypes...)>::weak_callback(callback<ResultType(ArgTypes...)> const& other) noexcept :
228 _impl(other._impl)
229{
230}
231
232template<typename ResultType, typename... ArgTypes>
233[[nodiscard]] inline callback<ResultType(ArgTypes...)> weak_callback<ResultType(ArgTypes...)>::lock() const noexcept
234{
235 return {_impl.lock()};
236}
237
238} // namespace v1
239} // namespace hi::v1
Functions and types for accessing operating system threads.
thread_id current_thread_id() noexcept
Get the current thread id.
@ other
The gui_event does not have associated data.
STL namespace.
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Definition callback.hpp:24
Definition callback.hpp:32
Definition callback.hpp:74
Definition callback.hpp:77
bool expired() const noexcept
Check if the callback object is expired.
Definition callback.hpp:112
A callback function.
Definition callback.hpp:153
Defer execution of a lambda to the end of the scope.
Definition defer.hpp:21
T empty(T... args)
T make_shared(T... args)
T move(T... args)
T push_back(T... args)