Skip to content

Commit bc42c88

Browse files
committed
Update DelegateMQ library
1 parent da8432d commit bc42c88

28 files changed

+904
-519
lines changed

DelegateMQ/DelegateMQ.h

Lines changed: 90 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,95 @@
55
// @see https://github.com/endurodave/DelegateMQ
66
// David Lafreniere, 2025.
77

8-
/// @file
9-
/// @brief DelegateMQ.h is a single include to obtain all delegate functionality.
8+
/// @file DelegateMQ.h
9+
/// @brief A single-include header for the complete DelegateMQ library functionality.
1010
///
11-
/// A C++ delegate library capable of invoking any callable function either synchronously
12-
/// or asynchronously on a user specified thread of control. It is also capable of calling
13-
/// a function remotely over any transport protocol.
14-
///
15-
/// Asynchronous function calls support both non-blocking and blocking modes with a timeout.
16-
/// The library supports all types of target functions, including free functions, class
17-
/// member functions, static class functions, lambdas, and `std::function`. It is capable of
18-
/// handling any function signature, regardless of the number of arguments or return value.
19-
/// All argument types are supported, including by value, pointers, pointers to pointers,
20-
/// and references. The delegate library takes care of the intricate details of function
21-
/// invocation across thread boundaries. Thread-safe delegate containers stores delegate
22-
/// instances with a matching function signature.
23-
///
24-
/// A delegate instance can be:
11+
/// @details
12+
/// DelegateMQ is a robust C++ delegate library that enables invoking any callable function
13+
/// (synchronously or asynchronously) on a specific user-defined thread of control. It also
14+
/// supports remote function invocation over any transport protocol.
2515
///
26-
/// * Copied freely.
27-
/// * Compared to same type delegatesand `nullptr`.
28-
/// * Reassigned.
29-
/// * Called.
30-
///
31-
/// Typical use cases are:
16+
/// **Key Features:**
17+
/// * **Universal Target Support:** Binds to free functions, class member functions, static functions,
18+
/// lambdas, and `std::function`.
19+
/// * **Any Signature:** Handles any function signature with any number of arguments or return values.
20+
/// * **Argument Safety:** Supports all argument types (value, pointer, pointer-to-pointer, reference)
21+
/// and safely marshals them across thread boundaries for asynchronous calls.
22+
/// * **Thread Control:** Unlike `std::async` (which uses a random thread pool), DelegateMQ executes
23+
/// the target function on a *specific* destination thread you control.
24+
/// * **Remote Invocation:** Capable of serializing arguments and invoking functions across network
25+
/// boundaries (UDP, TCP, ZeroMQ, etc.).
3226
///
33-
/// * Asynchronous Method Invocation(AMI)
34-
/// * Publish / Subscribe(Observer) Pattern
35-
/// * Anonymous, Asynchronous Thread - Safe Callbacks
36-
/// * Event - Driven Programming
37-
/// * Thread - Safe Asynchronous API
38-
/// * Design Patterns(Active Object)
27+
/// **Delegate Capabilities:**
28+
/// A delegate instance behaves like a first-class object:
29+
/// * **Copyable:** Can be copied freely.
30+
/// * **Comparable:** Supports equality checks against other delegates or `nullptr`.
31+
/// * **Assignable:** Can be reassigned at runtime.
32+
/// * **Callable:** Invoked via `operator()`.
3933
///
40-
/// The delegate library's asynchronous features differ from `std::async` in that the
41-
/// caller-specified thread of control is used to invoke the target function bound to
42-
/// the delegate, rather than a random thread from the thread pool. The asynchronous
43-
/// variants copy the argument data into the event queue, ensuring safe transport to the
44-
/// destination thread, regardless of the argument type. This approach provides 'fire and
45-
/// forget' functionality, allowing the caller to avoid waiting or worrying about
46-
/// out-of-scope stack variables being accessed by the target thread.
47-
///
48-
/// The `Async` and `AsyncWait` class variants may throw `std::bad_alloc` if heap allocation
49-
/// fails within `operator()(Args... args)`. Alternatively, define `DMQ_ASSERTS` to use `assert`
50-
/// as opposed to exceptions. All other delegate class functions do not throw exceptions.
51-
///
52-
/// Github repository location:
53-
/// https://github.com/endurodave/DelegateMQ
34+
/// **Common Use Cases:**
35+
/// * Asynchronous Method Invocation (AMI) on specific worker threads.
36+
/// * Publish / Subscribe (Observer) patterns.
37+
/// * Anonymous, thread-safe asynchronous callbacks.
38+
/// * Event-Driven Programming architectures.
39+
/// * Thread-Safe Asynchronous APIs.
40+
/// * Active Object design patterns.
5441
///
55-
/// See README.md, DETAILS.md, EXAMPLES.md, and source code Doxygen comments for more information.
42+
/// **Asynchronous Safety:**
43+
/// Asynchronous variants automatically copy argument data into the event queue. This provides true
44+
/// 'fire and forget' functionality, ensuring that out-of-scope stack variables in the caller
45+
/// do not cause data races or corruption in the target thread.
46+
///
47+
/// **Error Handling:**
48+
/// The `Async` and `AsyncWait` variants may throw `std::bad_alloc` if heap allocation fails during
49+
/// invocation. Alternatively, defining `DMQ_ASSERTS` switches error handling to assertions.
50+
/// All other delegate functions are `noexcept`.
51+
///
52+
/// **Documentation & Source:**
53+
/// * Repository: https://github.com/endurodave/DelegateMQ
54+
/// * See `README.md`, `DETAILS.md`, and `EXAMPLES.md` for comprehensive guides.
5655

57-
#include "delegate/DelegateOpt.h"
58-
#include "delegate/MulticastDelegateSafe.h"
59-
#include "delegate/UnicastDelegateSafe.h"
60-
#include "delegate/SignalSafe.h"
61-
#include "delegate/DelegateAsync.h"
62-
#include "delegate/DelegateAsyncWait.h"
56+
// -----------------------------------------------------------------------------
57+
// 1. Core Non-Thread-Safe Delegates
58+
// (Always available: Bare Metal, FreeRTOS, Windows, Linux)
59+
// -----------------------------------------------------------------------------
60+
#include "delegate/Delegate.h"
6361
#include "delegate/DelegateRemote.h"
62+
#include "delegate/MulticastDelegate.h"
63+
#include "delegate/UnicastDelegate.h"
64+
#include "delegate/Signal.h"
65+
66+
// -----------------------------------------------------------------------------
67+
// 2. Thread-Safe Wrappers (Mutex Only)
68+
// -----------------------------------------------------------------------------
69+
// - FreeRTOS: Uses FreeRTOSRecursiveMutex
70+
// - Bare Metal: Uses NullMutex
71+
// - StdLib: Uses std::recursive_mutex
72+
#if defined(DMQ_THREAD_STDLIB) || defined(DMQ_THREAD_FREERTOS) || defined(DMQ_THREAD_NONE)
73+
#include "delegate/MulticastDelegateSafe.h"
74+
#include "delegate/UnicastDelegateSafe.h"
75+
#include "delegate/SignalSafe.h"
76+
#endif
77+
78+
// -----------------------------------------------------------------------------
79+
// 3. Asynchronous "Fire and Forget" Delegates
80+
// -----------------------------------------------------------------------------
81+
// - FreeRTOS: OK
82+
// - Bare Metal: OK (Requires you to implement IThread wrapper for Event Loop)
83+
// - StdLib: OK
84+
#if defined(DMQ_THREAD_STDLIB) || defined(DMQ_THREAD_FREERTOS) || defined(DMQ_THREAD_NONE)
85+
#include "delegate/DelegateAsync.h"
86+
#endif
87+
88+
// -----------------------------------------------------------------------------
89+
// 4. Asynchronous "Blocking" Delegates (Wait for Result)
90+
// -----------------------------------------------------------------------------
91+
// - FreeRTOS: NO (Requires Semaphore/Condition Variable)
92+
// - Bare Metal: NO (Cannot block/sleep)
93+
// - StdLib: OK
94+
#if defined(DMQ_THREAD_STDLIB)
95+
#include "delegate/DelegateAsyncWait.h"
96+
#endif
6497

6598
#if defined(DMQ_THREAD_STDLIB)
6699
#include "predef/os/stdlib/Thread.h"
@@ -69,7 +102,7 @@
69102
#include "predef/os/freertos/Thread.h"
70103
#include "predef/os/freertos/ThreadMsg.h"
71104
#elif defined(DMQ_THREAD_NONE)
72-
// Create a custom application-specific thread
105+
// Bare metal: User must implement their own polling/interrupt logic
73106
#else
74107
#error "Thread implementation not found."
75108
#endif
@@ -119,8 +152,12 @@
119152
#endif
120153

121154
#include "predef/util/Fault.h"
122-
#include "predef/util/Timer.h"
123-
#include "predef/util/TransportMonitor.h"
124-
#include "predef/util/AsyncInvoke.h"
155+
156+
// Only include Timer and AsyncInvoke if threads exist
157+
#if !defined(DMQ_THREAD_NONE)
158+
#include "predef/util/Timer.h"
159+
#include "predef/util/AsyncInvoke.h"
160+
#include "predef/util/TransportMonitor.h"
161+
#endif
125162

126163
#endif

DelegateMQ/Predef.cmake

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,20 @@ if (DMQ_ALLOCATOR STREQUAL "ON")
7676
endif()
7777

7878
if (DMQ_UTIL STREQUAL "ON")
79-
file(GLOB UTIL_SOURCES
80-
"${DMQ_ROOT_DIR}/predef/util/*.c*"
81-
"${DMQ_ROOT_DIR}/predef/util/*.h"
82-
)
79+
if (DMQ_THREAD STREQUAL "DMQ_THREAD_NONE")
80+
# Bare metal: Only include utilities that DON'T need mutexes
81+
file(GLOB UTIL_SOURCES
82+
"${DMQ_ROOT_DIR}/predef/util/Fault.c*"
83+
"${DMQ_ROOT_DIR}/predef/util/Fault.h"
84+
# Explicitly exclude Timer.cpp and AsyncInvoke.cpp
85+
)
86+
else()
87+
# OS/RTOS present: Include everything
88+
file(GLOB UTIL_SOURCES
89+
"${DMQ_ROOT_DIR}/predef/util/*.c*"
90+
"${DMQ_ROOT_DIR}/predef/util/*.h"
91+
)
92+
endif()
8393
endif()
8494

8595
if (DMQ_ASSERTS STREQUAL "ON")

DelegateMQ/delegate/Delegate.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,36 @@
2626
/// The delegate library namespace
2727
namespace dmq {
2828

29+
namespace trait
30+
{
31+
// Helper trait to check if a type is a reference to a std::shared_ptr
32+
template <typename T>
33+
struct is_shared_ptr_reference : std::false_type {};
34+
35+
template <typename T>
36+
struct is_shared_ptr_reference<std::shared_ptr<T>&> : std::true_type {};
37+
38+
template <typename T>
39+
struct is_shared_ptr_reference<std::shared_ptr<T>*> : std::true_type {};
40+
41+
template <typename T>
42+
struct is_shared_ptr_reference<const std::shared_ptr<T>&> : std::true_type {};
43+
44+
template <typename T>
45+
struct is_shared_ptr_reference<const std::shared_ptr<T>* > : std::true_type {};
46+
47+
// Helper trait to check if a type is a double pointer (e.g., int**)
48+
template <typename T>
49+
struct is_double_pointer {
50+
// Remove 'const', 'volatile', and references first
51+
using RawT = std::remove_cv_t<std::remove_reference_t<T>>;
52+
53+
static constexpr bool value =
54+
std::is_pointer_v<RawT> &&
55+
std::is_pointer_v<std::remove_pointer_t<RawT>>;
56+
};
57+
}
58+
2959
/// @brief Non-template base class for all delegates.
3060
class DelegateBase {
3161
public:

DelegateMQ/delegate/DelegateAsync.h

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -55,36 +55,6 @@
5555

5656
namespace dmq {
5757

58-
namespace trait
59-
{
60-
// Helper trait to check if a type is a reference to a std::shared_ptr
61-
template <typename T>
62-
struct is_shared_ptr_reference : std::false_type {};
63-
64-
template <typename T>
65-
struct is_shared_ptr_reference<std::shared_ptr<T>&> : std::true_type {};
66-
67-
template <typename T>
68-
struct is_shared_ptr_reference<std::shared_ptr<T>*> : std::true_type {};
69-
70-
template <typename T>
71-
struct is_shared_ptr_reference<const std::shared_ptr<T>&> : std::true_type {};
72-
73-
template <typename T>
74-
struct is_shared_ptr_reference<const std::shared_ptr<T>* > : std::true_type {};
75-
76-
// Helper trait to check if a type is a double pointer (e.g., int**)
77-
template <typename T>
78-
struct is_double_pointer {
79-
// Remove 'const', 'volatile', and references first
80-
using RawT = std::remove_cv_t<std::remove_reference_t<T>>;
81-
82-
static constexpr bool value =
83-
std::is_pointer_v<RawT> &&
84-
std::is_pointer_v<std::remove_pointer_t<RawT>>;
85-
};
86-
}
87-
8858
/// @brief Stores all function arguments suitable for non-blocking asynchronous calls.
8959
/// Argument data is stored in the heap.
9060
/// @tparam Args The argument types of the bound delegate function.

0 commit comments

Comments
 (0)