The DelegateMQ C++ library enables function invocations on any callable, either synchronously, asynchronously, or on a remote endpoint. This messaging middleware library unifies function calls across threads, processes, or processors through a simple delegate interface. It provides well-defined abstract interfaces and numerous concrete examples, making it easy to port to any platform. As a header-only template library, DelegateMQ is thread-safe, unit-tested, and easy to use.
// Create an async delegate targeting lambda on thread1
auto lambda = [](int i) { std::cout << i; };
auto lambdaDelegate = dmq::MakeDelegate(std::function(lambda), thread1);
// Create an async delegate targeting Class::Func() on thread2
Class myClass;
auto memberDelegate = dmq::MakeDelegate(&myClass, &Class::Func, thread2);
// Create a thread-safe delegate container
dmq::MulticastDelegateSafe<void(int)> delegates;
// Insert delegates into the container
delegates += lambdaDelegate;
delegates += memberDelegate;
// Invoke all callable targets asynchronously
delegates(123);
In C++, a delegate function object encapsulates a callable entity, such as a function, method, or lambda, so it can be invoked later. A delegate is a type-safe wrapper around a callable function that allows it to be passed around, stored, or invoked at a later time, typically within different contexts or on different threads. Delegates are particularly useful for event-driven programming, callbacks, asynchronous APIs, or when you need to pass functions as arguments.
Synchronous and asynchronous delegates are available. Asynchronous variants handle both non-blocking and blocking modes with a timeout. The library supports all types of target functions, including free functions, class member functions, static class functions, lambdas, and std::function
. It is capable of handling any function signature, regardless of the number of arguments or return value. All argument types are supported, including by value, pointers, pointers to pointers, and references. The delegate library takes care of the intricate details of function invocation across thread boundaries. Concrete porting examples below with easy porting to others.
- Operating System: Windows, Linux, FreeRTOS
Remote delegates enable function invocation across processes or processors by using customizable serialization and transport mechanisms. All argument data is marshaled to support remote callable endpoints with any function signature. Minimal effort is required to extend support to any new library. Concrete examples are provided using support libraries below.
- Serialization: MessagePack, RapidJSON, MessageSerialize
- Transport: ZeroMQ, MQTT, UDP, data pipe, memory buffer
It is always safe to call the delegate. In its null state, a call will not perform any action and will return a default-constructed return value. A delegate behaves like a normal pointer type: it can be copied, compared for equality, called, and compared to nullptr
. Const correctness is maintained; stored const objects can only be called by const member functions.
A delegate instance can be:
- Copied freely.
- Compared to same type delegates and
nullptr
. - Reassigned.
- Called.
Typical use cases are:
- Synchronous and Asynchronous Callbacks
- Event-Driven Programming
- Inter-Process and Inter-Processor Communication
- Inter-Thread Publish/Subscribe (Observer) Pattern
- Thread-Safe Asynchronous API
- Asynchronous Method Invocation (AMI)
- Design Patterns (Active Object)
std::async
Thread Targeting
The DelegateMQ library asynchronous features differ from std::async
in that the caller-specified thread of control is used to invoke the target function bound to the delegate, rather than a random thread from the thread pool. The asynchronous variants copy the argument data into an event queue, ensuring safe transport to the destination thread, regardless of the argument type. This approach provides 'fire and forget' functionality, allowing the caller to avoid waiting or worrying about out-of-scope stack variables being accessed by the target thread.
DelegateMQ uses an external thread, transport, and serializer, all of which are based on simple, well-defined interfaces.
The library's flexible CMake build options allow for the inclusion of only the necessary features. Synchronous, asynchronous, and remote delegates can be used individually or in combination.
Originally published on CodeProject at Asynchronous Multicast Delegates in Modern C++ with a perfect 5.0 article feedback rating.
Systems are composed of various design patterns or libraries to implement callbacks, asynchronous APIs, and inter-thread or inter-processor communications. These elements typically lack shared commonality. Callbacks are one-off implementations by individual developers, messages between threads rely on OS message queues, and communication libraries handle data transfer complexities. However, the underlying commonality lies in the need to move argument data to the target handler function, regardless of its location.
The DelegateMQ middleware effectively encapsulates all data movement and function invocation within a single library. Whether the target function is a static method, class method, or lambda—residing locally in a separate thread or remotely on a different processor—the library ensures the movement of argument data (marshalling when necessary) and invokes the target function. The low-level details of data movement and function invocation are neatly abstracted from the application layer.
See Design Details for implementation design documentation and more examples.
See Doxygen Documentation for source code documentation.
See Unit Test Code Coverage test results.
CMake is used to create the build files. Visual Studio, GCC, and Clang toolchains are supported. Easy porting to other targets. Execute CMake console commands in the project root directory.
cmake -B build -S .
See Example Projects to build other project examples.
DelegateMQ at a glance.
Category | DelegateMQ |
---|---|
Purpose | DelegateMQ is a library used for invoking any callable synchronously, asynchronously, or remotely |
Usages | Callbacks (synchronous and asynchronous), asynchronous API's, communication and data distribution, and more |
Library | Allows customizing data sharing between threads, processes, or processors |
Complexity | Lightweight and extensible through external library interfaces and full source code |
Threading | No internal threads. External configurable thread interface portable to any OS (IThread ). |
Serialization | External configurable serialization data formats, such as MessagePack, RapidJSON, or custom encoding (ISerializer ) |
Transport | External configurable transport, such as ZeroMQ, TCP, UDP, serial, data pipe or any custom transport (ITransport ) |
Timeouts and Retries | Provided by a communication library (e.g. ZeroMQ REQ-REP (Request-Reply)), TCP/IP stack, or custom solution (ITransportMonitor ) |
Message Buffering | Provided by a communication library (e.g. ZeroMQ) or custom solution within transport |
Dynamic Memory | Heap or DelegateMQ fixed-block allocator |
Error Handling | Configurable for return error code, assert or exception |
Embedded Friendly | Yes. Any OS such as Windows, Linux and FreeRTOS. An OS is not required (i.e. "superloop"). |
Operation System | Any. Custom IThread implementation may be required. |
Language | C++17 or higher |
Alternative asynchronous implementations similar in concept to DelegateMQ, simpler, and less features.
- Asynchronous Callbacks in C++ - A C++ asynchronous callback framework.
- Asynchronous Callbacks in C - A C language asynchronous callback framework.
Repositories utilizing the DelegateMQ library.
- C++ State Machine with Asynchronous Delegates - a framework combining a C++ state machine with the asynchronous delegate library.
- Asynchronous State Machine Design in C++ - an asynchronous C++ state machine implemented using an asynchronous delegate library.
- Integration Test Framework using Google Test and Delegates - a multi-threaded C++ software integration test framework using Google Test and DelegateMQ libraries.
- Asynchronous SQLite API using C++ Delegates - an asynchronous SQLite wrapper implemented using an asynchronous delegate library.