VR-Engage  2.2
Loading...
Searching...
No Matches
delegate.h
Go to the documentation of this file.
1/*********************************************************************************
2** Copyright (c) 2025 MAK Technologies
3** All rights reserved.
4*********************************************************************************/
5
6#pragma once
7
8#include <functional>
9#include <type_traits>
10
11namespace makVre
12{
13//! \brief Modern delegate class that can bind and invoke callables with any number of parameters
14//! \tparam Ret Return type of the callback function
15//! \tparam Args Parameter types for the callback function (variadic)
16//!
17//! This class provides a unified interface for binding and invoking different types of callables:
18//! - Static/global functions
19//! - Member functions bound to objects
20//! - Lambda expressions
21//! - Functors (classes with operator())
22//!
23//! The delegate uses type erasure via std::function internally while maintaining the ability
24//! to compare and hash different delegate instances, which is useful for containers like
25//! std::unordered_map or for implementing event systems.
26//!
27//! \note Delegates are hashable and comparable, but due to limitations in C++, lambdas with
28//! identical code but at different source locations will be considered different delegates.
29template <typename Ret, typename... Args>
31{
32public:
33 //! \brief Enumeration of delegate types
34 //!
35 //! Categorizes the different types of callables that can be bound to a delegate.
36 enum class DelegateType
37 {
38 Empty, //!< Empty delegate with no bound callable
39 Static, //!< Static or global function
40 Member, //!< Member function bound to an object
41 Lambda //!< Lambda, functor, or other callable object
42 };
43
44 //! \brief Default constructor - creates an empty delegate
45 //!
46 //! Initializes a delegate with no bound callable. Calling an empty delegate
47 //! will return a default-constructed instance of the return type.
48 DtDelegate() = default;
49
50 //! \brief Constructor binding a static or global function
51 //! \param func Pointer to the static or global function to bind
52 //!
53 //! Creates a delegate that will invoke the specified static or global function
54 //! when called.
55 DtDelegate(Ret (*func)(Args...));
56
57 //! \brief Constructor binding a class member function
58 //! \tparam T Type of the object containing the member function
59 //! \param object Pointer to the object instance
60 //! \param method Pointer to the member function
61 //!
62 //! Creates a delegate that will invoke the specified member function on the
63 //! given object instance when called.
64 template <typename T, typename Method>
65 DtDelegate(T* object, Method method);
66
67 //! \brief Constructor binding a const class member function
68 //! \tparam T Type of the object containing the member function
69 //! \param object Pointer to the const object instance
70 //! \param method Pointer to the const member function
71 //!
72 //! Creates a delegate that will invoke the specified const member function on
73 //! the given const object instance when called.
74 template <typename T, typename Method>
75 DtDelegate(const T* object, Method method);
76
77 //! \brief Constructor for any callable (lambda, functor, etc.)
78 //! \tparam Callable Type of the callable object
79 //! \param callable The callable object to bind
80 //!
81 //! Creates a delegate that will invoke the specified lambda or functor when
82 //! called. The callable is stored by value, so any state within the callable
83 //! is preserved.
84 //!
85 //! \note SFINAE is used to prevent this constructor from matching when
86 //! a DtDelegate is passed, ensuring proper copy/move construction.
87 template <typename Callable, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Callable>, DtDelegate>>>
88 DtDelegate(Callable&& callable);
89
90 //! \brief Destructor
91 ~DtDelegate() = default;
92
93 //! \brief Copy constructor
94 //! \param other The delegate to copy from
95 //!
96 //! Creates a new delegate that is a copy of the given delegate, including its
97 //! bound callable and all associated hash information.
98 DtDelegate(const DtDelegate& other);
99
100 //! \brief Move constructor
101 //! \param other The delegate to move from
102 //!
103 //! Creates a new delegate by moving the contents of the given delegate,
104 //! leaving the source delegate in an empty state.
105 DtDelegate(DtDelegate&& other) noexcept;
106
107 //! \brief Copy assignment operator
108 //! \param other The delegate to copy from
109 //! \return Reference to this delegate after assignment
110 //!
111 //! Assigns the contents of the given delegate to this delegate, including its
112 //! bound callable and all associated hash information.
114
115 //! \brief Move assignment operator
116 //! \param other The delegate to move from
117 //! \return Reference to this delegate after assignment
118 //!
119 //! Moves the contents of the given delegate to this delegate, leaving the
120 //! source delegate in an empty state.
121 DtDelegate& operator=(DtDelegate&& other) noexcept;
122
123 //! \brief Reset the delegate to an empty state
124 //!
125 //! Clears the bound callable and resets all internal state, effectively
126 //! returning the delegate to the same state as if it were default constructed.
127 void reset() noexcept;
128
129 //! \brief Function call operator with perfect forwarding (non-void return type)
130 //! \tparam CallArgs Types of the arguments being passed to the delegate
131 //! \param args Arguments to forward to the bound callable
132 //! \return The result of invoking the bound callable with the given arguments
133 //!
134 //! Invokes the bound callable with the given arguments. If the delegate is empty:
135 //! - For default constructible types: returns default-constructed instance (Ret{})
136 //! - For non-default constructible types: throws std::runtime_error
137 //! This overload is enabled only when the return type is not void.
138 template <typename... CallArgs, typename = std::enable_if_t<!std::is_void_v<Ret>>>
139 Ret operator()(CallArgs&&... args) const;
140
141 //! \brief Function call operator with perfect forwarding (void return type)
142 //! \tparam CallArgs Types of the arguments being passed to the delegate
143 //! \param args Arguments to forward to the bound callable
144 //!
145 //! Invokes the bound callable with the given arguments. If the delegate is empty,
146 //! this function simply returns without doing anything (silent no-op).
147 //! This overload is enabled only when the return type is void.
148 template <typename... CallArgs, typename = std::enable_if_t<std::is_void_v<Ret>>, typename = void>
149 void operator()(CallArgs&&... args) const;
150
151 //! \brief Check if the delegate has a bound callable
152 //! \return true if a callable is bound, false otherwise
153 //!
154 //! Allows the delegate to be used in boolean contexts to check if it has a
155 //! bound callable.
156 explicit operator bool() const noexcept;
157
158 //! \brief Get hash value for this delegate
159 //! \return Hash value representing the delegate's bound callable
160 //!
161 //! Computes and returns a hash value that uniquely identifies the bound callable.
162 //! This value is cached after computation for efficiency.
163 //!
164 //! \note For static functions and member functions, this will reliably reflect
165 //! the identity of the function. For lambdas and functors, the hash is based on
166 //! the object's address and type, which may differ between identical lambdas
167 //! defined at different source locations.
168 std::size_t hash() const noexcept;
169
170 //! \brief Equality comparison operator using hash values
171 //! \param rhs Right-hand side delegate to compare with
172 //! \return true if delegates are equivalent, false otherwise
173 //!
174 //! Compares this delegate with another for equality based on their hash values.
175 //! Two delegates are considered equivalent if they have the same type and:
176 //! - For static functions: they point to the same function
177 //! - For member functions: they point to the same method of the same object
178 //! - For lambdas/functors: they are the same instance (by address and type)
179 //!
180 //! \note std::function does not support deep comparison of bound callables, so
181 //! this comparison is based on the computed hash values.
182 bool operator==(const DtDelegate& rhs) const noexcept;
183
184 //! \brief Inequality comparison operator
185 //! \param rhs Right-hand side delegate to compare with
186 //! \return true if delegates are not equivalent, false otherwise
187 //!
188 //! Inverts the result of the equality operator.
189 bool operator!=(const DtDelegate& rhs) const noexcept;
190
191 //! \brief Get the type of this delegate
192 //! \return The type of delegate (Empty, Static, Member, or Lambda)
193 //!
194 //! This function returns the type of the delegate, which can be useful for
195 //! debugging and introspection. The type indicates what kind of callable
196 //! entity is bound to the delegate:
197 //! - Empty: No callable is bound
198 //! - Static: A static or global function is bound
199 //! - Member: A member function bound to an object is bound
200 //! - Lambda: A lambda, functor, or other callable is bound
201 DelegateType getType() const noexcept;
202
203 //! \brief Helper function to hash method pointers
204 //! \tparam Method Type of the method pointer
205 //! \param method The method pointer to hash
206 //! \return Hash value for the method pointer
207 //!
208 //! Creates a hash value from a method pointer by converting it to a byte sequence
209 //! and hashing that sequence. This allows distinguishing between different methods
210 //! of the same class.
211 template <typename Method>
212 static std::size_t hashMethodPtr(const Method& method) noexcept;
213
214 //! \brief Helper function to hash any pointer (object or function)
215 //! \tparam PtrType Type of the pointer
216 //! \param ptr The pointer to hash
217 //! \return Hash value for the pointer
218 //!
219 //! Creates a hash value from a pointer by using std::hash<const void*>.
220 //! This is used to hash object pointers and function pointers.
221 template <typename PtrType>
222 static std::size_t hashPtr(PtrType ptr) noexcept;
223
224 //! \brief Helper function to hash lambda objects
225 //! \tparam Callable Type of the callable object
226 //! \param callable The callable object to hash
227 //! \return Hash value for the callable object
228 //!
229 //! Creates a hash value for a lambda or functor by combining the object's address
230 //! and its type information. This allows distinguishing between different lambda
231 //! expressions, even if they have identical code.
232 template <typename Callable>
233 static std::size_t hashLambda(const Callable& callable) noexcept;
234
235protected:
236 //! \brief Compute hash for the current delegate state
237 //!
238 //! Calculates a hash value based on the delegate's type and the specific bound callable.
239 //! The hash computation differs based on the delegate type:
240 //! - Empty: Hash is 0
241 //! - Static: Hash is derived from the function pointer
242 //! - Member: Hash combines the object pointer and method pointer
243 //! - Lambda: Hash combines the lambda instance and its type
244 //!
245 //! Once computed, the hash is cached for future use.
246 void computeHash() noexcept;
247
248 //! \brief Reset hash computation state
249 //!
250 //! Clears the cached hash value and marks it as not computed, ensuring that
251 //! the next call to hash() will recompute the value.
252 void resetHash() const noexcept;
253
254protected:
255 //! \brief Function object to store any callable
256 //!
257 //! Uses std::function for type erasure, allowing the delegate to store and
258 //! invoke any callable with the matching signature.
259 std::function<Ret(Args...)> myFunction = nullptr;
260
261 //! \brief Type of delegate
262 //!
263 //! Stores the category of the bound callable (Empty, Static, Member, or Lambda).
265
266 //! \brief Hash of object pointer for member function delegates
267 //!
268 //! Stores the hash of the object pointer when binding a member function.
269 //! Used to differentiate delegates bound to different object instances.
270 std::size_t myObjectHash = 0;
271
272 //! \brief Hash of static function pointer for static function delegates
273 //!
274 //! Stores the hash of the function pointer when binding a static function.
275 //! Used to identify the specific function being called.
276 std::size_t myStaticFuncHash = 0;
277
278 //! \brief Hash value for method pointer to distinguish different member functions
279 //!
280 //! Stores the hash of the method pointer when binding a member function.
281 //! Used to differentiate between different methods of the same class.
282 std::size_t myMethodHash = 0;
283
284 //! \brief Hash value for lambda objects
285 //!
286 //! Stores the hash of the lambda or functor when binding a callable object.
287 //! Used to identify the specific callable being invoked.
288 std::size_t myLambdaHash = 0;
289
290 //! \brief Hash value for the delegate's bound callable
291 //!
292 //! Caches the computed hash value for efficient repeated access.
293 //! This value combines information based on the delegate type.
294 mutable std::size_t myHash = 0;
295
296 //! \brief Flag to track if hash has been computed
297 //!
298 //! Indicates whether the hash value has been computed and cached.
299 //! Used to avoid unnecessary recomputation of the hash.
300 mutable bool myHashComputed = false;
301};
302
303} // namespace makVre
304
305// Include template implementation
306#include "vreUtil/delegate.inl"
std::size_t hash() const noexcept
DtDelegate(T *object, Method method)
Constructor binding a class member function.
DelegateType myType
Definition delegate.h:264
DtDelegate & operator=(DtDelegate &&other) noexcept
Move assignment operator.
std::size_t myStaticFuncHash
Definition delegate.h:276
std::size_t myLambdaHash
Definition delegate.h:288
void resetHash() const noexcept
~DtDelegate()=default
Destructor.
DelegateType getType() const noexcept
DtDelegate()=default
Default constructor - creates an empty delegate.
DtDelegate(const DtDelegate &other)
Copy constructor.
DtDelegate(Callable &&callable)
Constructor for any callable (lambda, functor, etc.)
static std::size_t hashMethodPtr(const Method &method) noexcept
DtDelegate(const T *object, Method method)
Constructor binding a const class member function.
DtDelegate(DtDelegate &&other) noexcept
Move constructor.
std::size_t myHash
Definition delegate.h:294
bool myHashComputed
Definition delegate.h:300
void reset() noexcept
Reset the delegate to an empty state.
std::function< void(Args...)> myFunction
Definition delegate.h:259
static std::size_t hashLambda(const Callable &callable) noexcept
std::size_t myObjectHash
Definition delegate.h:270
static std::size_t hashPtr(PtrType ptr) noexcept
DelegateType
Enumeration of delegate types.
Definition delegate.h:37
@ Lambda
Lambda, functor, or other callable object.
Definition delegate.h:41
@ Static
Static or global function.
Definition delegate.h:39
@ Member
Member function bound to an object.
Definition delegate.h:40
@ Empty
Empty delegate with no bound callable.
Definition delegate.h:38
DtDelegate(Ret(*func)(Args...))
Constructor binding a static or global function.
DtDelegate & operator=(const DtDelegate &other)
Copy assignment operator.
std::size_t myMethodHash
Definition delegate.h:282
Include export definitions for this library.
Definition glsVreMessageUtil.h:49