27 template <
typename... TParameterTypes>
28 class TContractProvider
33 TContractProvider *provider =
nullptr;
43 friend class TContractProvider<TParameterTypes...>;
47 using Job = std::function<void(TParameterTypes...)>;
50 std::shared_ptr<Job> _job =
nullptr;
51 std::shared_ptr<State> _state;
53 Contract(TContractProvider *p_originator,
const std::shared_ptr<Job> &p_job) :
55 _state(p_originator ? p_originator->_state : nullptr)
70 _job(std::move(p_other._job)),
71 _state(std::move(p_other._state))
74 p_other._state.reset();
90 _job = std::move(p_other._job);
91 _state = std::move(p_other._state);
93 p_other._state.reset();
113 return (_job && *_job);
124 throw std::runtime_error(
"Can't resign an already resigned contract.");
128 if (_state && _state->provider)
130 _state->provider->unsubscribe(*
this);
145 throw std::runtime_error(
"Can't relinquish an already resigned contract.");
147 if (_state && _state->provider)
149 _state->provider->relinquish(std::move(*
this));
163 (*_job)(std::forward<TParameterTypes>(p_args)...);
174 friend class TContractProvider<TParameterTypes...>;
194 _state(std::move(p_other._state)),
206 if (
this != &p_other)
209 _state = std::move(p_other._state);
210 _mode = p_other._mode;
226 return (_state && _state->provider !=
nullptr);
243 auto p_state = std::move(_state);
249 if (
auto *prov = p_state->provider)
251 prov->_releaseBlock(_mode);
256 std::shared_ptr<State> _state;
259 Blocker(std::shared_ptr<State> p_state,
Mode p_mode) :
260 _state(
std::move(p_state)),
272 std::vector<std::shared_ptr<Job>> _subscribedJobs;
273 std::vector<Contract> _relinquishedContracts;
275 std::shared_ptr<State> _state = std::make_shared<State>(State{
this});
277 mutable std::recursive_mutex _mutex;
279 mutable std::size_t _ignoreCount = 0;
280 mutable std::size_t _delayCount = 0;
282 using ArgTuple = std::tuple<std::decay_t<TParameterTypes>...>;
283 mutable std::optional<ArgTuple> _pendingDelayed;
297 _state->provider =
nullptr;
310 std::lock_guard<std::recursive_mutex> lock(p_other._mutex);
312 _subscribedJobs = std::move(p_other._subscribedJobs);
313 _relinquishedContracts = std::move(p_other._relinquishedContracts);
314 _state = std::move(p_other._state);
315 _ignoreCount = p_other._ignoreCount;
316 _delayCount = p_other._delayCount;
317 _pendingDelayed = std::move(p_other._pendingDelayed);
321 _state->provider =
this;
324 p_other._subscribedJobs.clear();
325 p_other._relinquishedContracts.clear();
326 p_other._ignoreCount = 0;
327 p_other._delayCount = 0;
328 p_other._pendingDelayed = std::nullopt;
329 p_other._state = std::make_shared<State>(State{&p_other});
337 TContractProvider &
operator=(TContractProvider &&p_other)
noexcept
339 if (
this != &p_other)
341 std::scoped_lock lock(_mutex, p_other._mutex);
346 _state->provider =
nullptr;
349 _subscribedJobs = std::move(p_other._subscribedJobs);
350 _relinquishedContracts = std::move(p_other._relinquishedContracts);
351 _state = std::move(p_other._state);
352 _ignoreCount = p_other._ignoreCount;
353 _delayCount = p_other._delayCount;
354 _pendingDelayed = std::move(p_other._pendingDelayed);
358 _state->provider =
this;
361 p_other._subscribedJobs.clear();
362 p_other._relinquishedContracts.clear();
363 p_other._ignoreCount = 0;
364 p_other._delayCount = 0;
365 p_other._pendingDelayed = std::nullopt;
366 p_other._state = std::make_shared<State>(State{&p_other});
378 std::lock_guard<std::recursive_mutex> lock(_mutex);
380 return Blocker(_state, p_mode);
388 std::lock_guard<std::recursive_mutex> lock(_mutex);
389 for (
auto &job : _subscribedJobs)
396 _subscribedJobs.clear();
397 _relinquishedContracts.clear();
398 _pendingDelayed.reset();
408 std::lock_guard<std::recursive_mutex> lock(_mutex);
409 auto toAdd = std::make_shared<Job>(p_job);
410 _subscribedJobs.push_back(toAdd);
411 return Contract(
this, toAdd);
414 template <
typename Dummy = void,
typename = std::enable_if_t<(
sizeof...(TParameterTypes) > 0), Dummy>>
422 return subscribe([p_job](TParameterTypes...) {
433 std::lock_guard<std::recursive_mutex> lock(_mutex);
434 _relinquishedContracts.push_back(std::move(p_contract));
443 std::lock_guard<std::recursive_mutex> lock(_mutex);
444 auto it = std::remove(_subscribedJobs.begin(), _subscribedJobs.end(), p_contract._job);
445 if (it != _subscribedJobs.end())
451 _subscribedJobs.erase(it, _subscribedJobs.end());
461 std::lock_guard<std::recursive_mutex> lock(_mutex);
465 _pendingDelayed.emplace(std::forward<TParameterTypes>(p_args)...);
469 if (_ignoreCount > 0)
474 for (
auto &job : _subscribedJobs)
478 (*job)(std::forward<TParameterTypes>(p_args)...);
489 return (_subscribedJobs.size());
500 _pendingDelayed.reset();
513 std::lock_guard<std::recursive_mutex> lock(_mutex);
517 if (_ignoreCount > 0)
529 if (_ignoreCount == 0 && _delayCount == 0 && _pendingDelayed.has_value())
531 auto args = std::move(*_pendingDelayed);
533 _pendingDelayed.reset();
535 std::apply([
this](
auto &&...p_xs) {
536 this->
trigger(std::forward<
decltype(p_xs)>(p_xs)...);
RAII helper that ignores or delays triggers while active.
Definition spk_contract_provider.hpp:173
void release() noexcept
Releases the block, applying deferred triggers when appropriate.
Definition spk_contract_provider.hpp:241
bool active() const noexcept
Checks if this blocker is attached to a provider.
Definition spk_contract_provider.hpp:224
Mode
Strategies controlling how callbacks are treated while blocked.
Definition spk_contract_provider.hpp:179
@ Delay
Definition spk_contract_provider.hpp:181
@ Ignore
Definition spk_contract_provider.hpp:180
Blocker & operator=(Blocker &&p_other) noexcept
Move-assigns a blocker, releasing current ownership first.
Definition spk_contract_provider.hpp:204
Mode mode() const noexcept
Gets the blocking mode.
Definition spk_contract_provider.hpp:233
Blocker(Blocker &&p_other) noexcept
Move-constructs a blocker, transferring block ownership.
Definition spk_contract_provider.hpp:193
Handle to a subscribed job that can be resigned or triggered.
Definition spk_contract_provider.hpp:42
std::function< void(TParameterTypes...)> Job
Callback signature accepted by the provider.
Definition spk_contract_provider.hpp:47
bool isValid() const
Checks whether the contract currently owns a valid job.
Definition spk_contract_provider.hpp:111
~Contract()
Resigns the contract upon destruction when still subscribed.
Definition spk_contract_provider.hpp:99
Contract(Contract &&p_other) noexcept
Move-constructs the contract, transferring ownership.
Definition spk_contract_provider.hpp:69
void trigger(TParameterTypes... p_args) const
Directly invokes the job if valid.
Definition spk_contract_provider.hpp:159
Contract & operator=(Contract &&p_other) noexcept
Move-assigns the contract, resigning any current subscription.
Definition spk_contract_provider.hpp:82
void relinquish()
Transfers ownership back to the provider without immediate unsubscribe.
Definition spk_contract_provider.hpp:141
void resign()
Unsubscribes the contract and drops ownership.
Definition spk_contract_provider.hpp:120
Publishes callbacks (contracts) and allows blocking, delaying, or triggering them with arguments.
Definition spk_contract_provider.hpp:29
void invalidateContracts()
Invalidates and clears all contracts.
Definition spk_contract_provider.hpp:386
size_t nbContracts() const
Returns the number of currently subscribed contracts.
Definition spk_contract_provider.hpp:487
void trigger(TParameterTypes... p_args) const
Triggers all subscribed jobs unless blocked/delayed.
Definition spk_contract_provider.hpp:459
TContractProvider(TContractProvider &&p_other) noexcept
Move-constructs, transferring subscriptions and state.
Definition spk_contract_provider.hpp:308
~TContractProvider()
Destroys the provider and invalidates all contracts.
Definition spk_contract_provider.hpp:291
std::function< void()> VoidJob
Callback used for providers without parameters.
Definition spk_contract_provider.hpp:269
Contract subscribe(const VoidJob &p_job)
Subscribes a job without parameters, wrapping it to match the signature.
Definition spk_contract_provider.hpp:420
TContractProvider & operator=(TContractProvider &&p_other) noexcept
Move-assigns, releasing current contracts and adopting the source state.
Definition spk_contract_provider.hpp:337
void unsubscribe(const Contract &p_contract)
Unsubscribes a contract, removing it from the list.
Definition spk_contract_provider.hpp:441
Contract subscribe(const Job &p_job)
Subscribes a job and returns its contract handle.
Definition spk_contract_provider.hpp:406
typename Contract::Job Job
Alias to the subscribed callback signature.
Definition spk_contract_provider.hpp:267
Blocker block(typename Blocker::Mode p_mode=Blocker::Mode::Ignore)
Creates a blocker in the given mode.
Definition spk_contract_provider.hpp:376
void relinquish(Contract &&p_contract)
Moves a contract into the relinquished list (no immediate unsubscribe).
Definition spk_contract_provider.hpp:431