CallbackList is the fundamental class in eventpp. The other classes EventDispatcher and EventQueue are built on CallbackList.
CallbackList holds a list of callbacks. At the time of the call, CallbackList simply invokes each callback one by one. Consider CallbackList as the signal/slot system in Qt, or the callback function pointer in some Windows APIs (such as lpCompletionRoutine in ReadFileEx
).
The callback can be any callback target -- functions, pointers to functions, , pointers to member functions, lambda expressions, and function objects.
eventpp/callbacklist.h
template <
typename Prototype,
typename Policies = DefaultPolicies
>
class CallbackList;
Prototype
: the callback prototype. It's C++ function type such as void(int, std::string, const MyClass *)
.
Policies
: the policies to configure and extend the callback list. The default value is DefaultPolicies
. See document of policies for details.
Handle
: the handle type returned by append
, prepend
and insert
. A handle can be used to insert a callback or remove a callback. To check if a Handle
is empty, convert it to boolean, false is empty. Handle
is copyable.
Callback
: the callback storage type.
CallbackList() noexcept;
CallbackList(const CallbackList & other);
CallbackList(CallbackList && other) noexcept;
CallbackList & operator = (const CallbackList & other);
CallbackList & operator = (CallbackList && other) noexcept;
void swap(CallbackList & other) noexcept;
CallbackList can be copied, moved, assigned, move assigned, and swapped.
bool empty() const;
Return true if the callback list is empty.
Note: in multi threading, this function returning true doesn't guarantee that the list is empty. The list may immediately become non-empty after the function returns true, and vice versa.
operator bool() const;
Return true if the callback list is not empty.
This operator allows a CallbackList instance be used in condition statement.
Handle append(const Callback & callback);
Add the callback to the callback list.
The callback is added to the end of the callback list.
Return a handle that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.
If append
is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.
The time complexity is O(1).
Handle prepend(const Callback & callback);
Add the callback to the callback list.
The callback is added to the beginning of the callback list.
Return a handle that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.
If prepend
is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.
The time complexity is O(1).
Handle insert(const Callback & callback, const Handle & before);
Insert the callback to the callback list before the callback handle before. If before is not found, callback is added at the end of the callback list.
Return a handle that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.
If insert
is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.
The time complexity is O(1).
Note: the caller must ensure the handle before
is created by this
CallbackList. If the caller can't ensure it, ownsHandle
can be used
to check if the handle before
belongs to this
CallbackList. The function insert
can only be called if ownsHandle(before)
returns true,
otherwise, it's undefined behavior and it causes weird bugs.
insert
only assert(ownsHandle(before))
, but there is no check in release code for performance reason.
bool remove(const Handle & handle);
Remove the callback handle from the callback list.
Return true if the callback is removed successfully, false if the callback is not found.
The time complexity is O(1).
Note: the handle
must be created by this
CallbackList. See the note in function insert
for details.
bool ownsHandle(const Handle & handle) const;
Return true if the handle
is created by the CallbackList, false if not.
The time complexity is O(N).
template <typename Func>
void forEach(Func && func) const;
Apply func
to all callbacks.
The func
can be one of the two prototypes:
AnyReturnType func(const CallbackList::Handle &, const CallbackList::Callback &);
AnyReturnType func(const CallbackList::Callback &);
Note: the func
can remove any callbacks, or add other callbacks, safely.
template <typename Func>
bool forEachIf(Func && func) const;
Apply func
to all callbacks. func
must return a boolean value, and if the return value is false, forEachIf stops the looping immediately.
Return true
if all callbacks are invoked, or event
is not found, false
if func
returns false
.
void operator() (Args ...args) const;
Invoke each callbacks in the callback list.
The callbacks are called with arguments args
.
The callbacks are called in the thread same as the callee of operator()
.
- If a callback adds another callback to the callback list during a invoking, the new callback is guaranteed not to be triggered within the same invoking. This is guaranteed by an unsigned 64 bits integer counter. This rule will be broken is the counter is overflowed to zero in a invoking, but this rule will continue working on the subsequence invoking.
- Any callbacks that are removed during a invoking are guaranteed not triggered.
- All above points are not true in multiple threading. That's to say, if one thread is invoking a callback list, the other thread add or remove a callback, the added or removed callback may be called during the invoking.
append
: O(1)prepend
: O(1)insert
: O(1)remove
: O(1)
CallbackList uses doubly linked list to manage the callbacks.
Each node is linked by a shared pointer. Using shared pointer allows nodes to be removed during iterating.