{"id":21274162,"url":"https://github.com/endurodave/DelegateMQ","last_synced_at":"2025-07-11T06:33:50.082Z","repository":{"id":44376229,"uuid":"256810345","full_name":"endurodave/AsyncMulticastDelegateModern","owner":"endurodave","description":"Asynchronous Multicast Delegates in Modern C++","archived":false,"fork":false,"pushed_at":"2024-11-13T13:17:17.000Z","size":655,"stargazers_count":9,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-13T14:25:37.618Z","etag":null,"topics":["asynchronous-callbacks","asynchronous-programming","callback-functions","cpp","cpp17","cross-platform","delegates","embedded","linux","multi-thread","multicast","publish-subscribe","template-metaprogramming","variadic-templates","windows"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/endurodave.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-18T17:16:07.000Z","updated_at":"2024-11-13T13:17:21.000Z","dependencies_parsed_at":"2024-01-21T15:42:47.950Z","dependency_job_id":"1516d315-d9da-49b3-83ac-d65a09b38e19","html_url":"https://github.com/endurodave/AsyncMulticastDelegateModern","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsyncMulticastDelegateModern","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsyncMulticastDelegateModern/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsyncMulticastDelegateModern/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FAsyncMulticastDelegateModern/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/endurodave","download_url":"https://codeload.github.com/endurodave/AsyncMulticastDelegateModern/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225701547,"owners_count":17510555,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous-callbacks","asynchronous-programming","callback-functions","cpp","cpp17","cross-platform","delegates","embedded","linux","multi-thread","multicast","publish-subscribe","template-metaprogramming","variadic-templates","windows"],"created_at":"2024-11-21T09:19:11.790Z","updated_at":"2025-07-11T06:33:50.068Z","avatar_url":"https://github.com/endurodave.png","language":"C++","readme":"![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue)\n[![conan Ubuntu](https://github.com/endurodave/DelegateMQ/actions/workflows/cmake_ubuntu.yml/badge.svg)](https://github.com/endurodave/DelegateMQ/actions/workflows/cmake_ubuntu.yml)\n[![conan Clang](https://github.com/endurodave/DelegateMQ/actions/workflows/cmake_clang.yml/badge.svg)](https://github.com/endurodave/DelegateMQ/actions/workflows/cmake_clang.yml)\n[![conan Windows](https://github.com/endurodave/DelegateMQ/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/endurodave/DelegateMQ/actions/workflows/cmake_windows.yml)\n[![Codecov](https://codecov.io/gh/endurodave/DelegateMQ/branch/master/graph/badge.svg)](https://app.codecov.io/gh/endurodave/DelegateMQ)\n\n# Delegates in C++\n\nDelegateMQ is a C++ header-only library for invoking any callable (e.g., function, method, lambda):\n\n- Synchronously\n- Asynchronously\n- Remotely across processes or processors\n\nIt unifies function calls across threads or systems via a simple delegate interface. DelegateMQ is thread-safe, unit-tested, and easy to port to any platform.\n\n# Key Concepts\n\n- `MakeDelegate` – Creates a delegate instance bound to a callable (lambda, function, or method).\n- `MulticastDelegateSafe` – A thread-safe container of delegates, allowing broadcast-style invocation.\n- `Thread` – A cross-platform thread class capable of asynchronous delegate invocation.\n\n# Examples\n\nInvoke `MsgOut()` using delegates.\n\n```cpp\n#include \"DelegateMQ.h\"\n\nsize_t MsgOut(const std::string\u0026 msg)\n{\n    std::cout \u003c\u003c msg \u003c\u003c std::endl;\n    return msg.size();\n}\n\nint main(void)\n{\n    Thread thread(\"WorkerThread\");\n    thread.CreateThread();\n\n    auto sync = dmq::MakeDelegate(\u0026MsgOut);\n    sync(\"Invoke MsgOut sync!\");\n\n    auto async = dmq::MakeDelegate(\u0026MsgOut, thread);\n    async(\"Invoke MsgOut async (non-blocking)!\");\n\n    auto asyncWait = dmq::MakeDelegate(\u0026MsgOut, thread, dmq::WAIT_INFINITE);\n    size_t size = asyncWait(\"Invoke MsgOut async wait (blocking)!\");\n\n    auto asyncWait1s = dmq::MakeDelegate(\u0026MsgOut, thread, std::chrono::seconds(1));\n    auto retVal = asyncWait1s.AsyncInvoke(\"Invoke MsgOut async wait (blocking max 1s)!\");\n    if (retVal.has_value())     // Async invoke completed within 1 second?\n        size = retVal.value();  // Get return value\n\n    std::ostringstream stream(std::ios::out | std::ios::binary);\n    Dispatcher dispatcher;\n    Serializer\u003cvoid(const std::string\u0026)\u003e serializer;\n    \n    dmq::DelegateFreeRemote\u003cvoid(const std::string\u0026)\u003e remote(dmq::DelegateRemoteId(1));\n    remote.SetStream(\u0026stream);\n    remote.SetDispatcher(\u0026dispatcher);\n    remote.SetSerializer(\u0026serializer);\n    remote(\"Invoke MsgOut remote!\");\n\n    return 0;\n}\n```\n\n`Subscriber` registers with `Publisher` to receive asynchronous callbacks.\n\n```cpp\nclass Publisher\n{\npublic:\n    // Thread-safe container to store registered callbacks\n    dmq::MulticastDelegateSafe\u003cvoid(const std::string\u0026 msg)\u003e MsgCb;\n\n    static Publisher\u0026 Instance() \n    {\n        static Publisher instance;\n        return instance;\n    }\n\n    void SetMsg(const std::string\u0026 msg) \n    {\n        m_msg = msg;    // Store message\n        MsgCb(m_msg);   // Invoke all registered callbacks\n    }\n\nprivate:\n    Publisher() = default;\n    std::string m_msg;\n};\n\nclass Subscriber\n{\npublic:\n    Subscriber() : m_thread(\"SubscriberThread\") \n    {\n        m_thread.CreateThread();\n\n        // Register for publisher async callback on m_thread context\n        Publisher::Instance().MsgCb += dmq::MakeDelegate(this, \u0026Subscriber::HandleMsgCb, m_thread);\n    }\n\nprivate:\n    // Handle publisher callback on m_thread\n    void HandleMsgCb(const std::string\u0026 msg) { std::cout \u003c\u003c msg \u003c\u003c std::endl; }\n    Thread m_thread;\n};\n```\n\nMultiple callables targets stored and invoked asynchronously.\n\n```cpp\n// Create an async delegate targeting lambda on thread1\nauto lambda = [](int i) { std::cout \u003c\u003c i; };\nauto lambdaDelegate = dmq::MakeDelegate(std::function(lambda), thread1);\n\n// Create an async delegate targeting Class::Func() on thread2\nClass myClass;\nauto memberDelegate = dmq::MakeDelegate(\u0026myClass, \u0026Class::Func, thread2);\n\n// Create a thread-safe delegate container\ndmq::MulticastDelegateSafe\u003cvoid(int)\u003e delegates;\n\n// Insert delegates into the container \ndelegates += lambdaDelegate;\ndelegates += memberDelegate;\n\n// Invoke all callable targets asynchronously \ndelegates(123);\n```\n\nAsynchronous public API reinvokes `StoreAsync()` call onto the internal `m_thread` context.\n\n```cpp\n// Store data using asynchronous public API. Class is thread-safe.\nclass DataStore\n{\npublic:\n    DataStore() : m_thread(\"DataStoreThread\")\n    {\n        m_thread.CreateThread();\n    }\n\n    // Store data asynchronously on m_thread context (non-blocking)\n    void StoreAsync(const Data\u0026 data)\n    {\n        // If the caller thread is not the internal thread, reinvoke this function \n        // asynchronously on the internal thread to ensure thread-safety\n        if (m_thread.GetThreadId() != Thread::GetCurrentThreadId()) \n        {\n            // Reinvoke StoreAsync(data) on m_thread context\n            return dmq::MakeDelegate(this, \u0026DataStore::StoreAsync, m_thread)(data);\n        }\n        m_data = data;  // Data stored on m_thread context\n    }\n\nprivate:\n    Data m_data;        // Data storage\n    Thread m_thread;    // Internal thread\n};\n```\n\n# Overview\n\nIn 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.\n\nOriginally published on CodeProject at \u003ca href=\"https://www.codeproject.com/Articles/5277036/Asynchronous-Multicast-Delegates-in-Modern-Cpluspl\"\u003eAsynchronous Multicast Delegates in Modern C++\u003c/a\u003e with a perfect 5.0 article feedback rating.\n\n## Sync Delegates\n\nSynchronous delegates invoke the target function anonymously within the current execution context. No external library dependencies are required.\n\n## Async Delegates\n\nAsynchronous delegates support both non-blocking and blocking invocation modes, including optional timeouts. The library enables anonymous invocation of any callable target, regardless of the number or type of arguments, or the presence of a return value. All argument types are supported — including by value, pointers, pointer-to-pointer, and references.\n\nThe delegate library abstracts the complexities of invoking functions across thread boundaries. Concrete examples are provided below, with straightforward paths to porting across platforms.\n\n* **Operating System:** Windows, Linux, [FreeRTOS](https://github.com/FreeRTOS/FreeRTOS)\n\n## Remote Delegates\n\nRemote delegates enable function invocation across processes or processors using customizable serialization and transport mechanisms. All argument data is marshaled to support remote callables with any function signature. The design allows easy integration with new transport or serialization libraries. Concrete examples using supported libraries are provided below. \n\n* **Serialization:** [MessagePack](https://msgpack.org/index.html), [RapidJSON](https://github.com/Tencent/rapidjson), [Cereal](https://github.com/USCiLab/cereal), [Bitsery](https://github.com/fraillt/bitsery), [MessageSerialize](https://github.com/endurodave/MessageSerialize)\n* **Transport:** [ZeroMQ](https://zeromq.org/), [NNG](https://github.com/nanomsg/nng), [MQTT](https://github.com/eclipse-paho/paho.mqtt.c), UDP, data pipe, memory buffer\n \n## Delegate Semantics and Use Cases\n\nIt 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.\n\n A delegate instance can be:\n\n * Copied freely.\n * Compared to same type delegates and `nullptr`.\n * Reassigned.\n * Called.\n \nTypical use cases are:\n\n* Synchronous and Asynchronous Callbacks\n* Event-Driven Programming\n* Inter-Process and Inter-Processor Communication \n* Inter-Thread Publish/Subscribe (Observer) Pattern\n* Thread-Safe Asynchronous API\n* Asynchronous Method Invocation (AMI) \n* Design Patterns (Active Object)\n* `std::async` Thread Targeting\n\n# Modular Architecture\n\nDelegateMQ uses an external thread, transport, and serializer, all of which are based on simple, well-defined interfaces.\n\n\u003cimg src=\"docs/LayerDiagram.jpg\" alt=\"DelegateMQ Layer Diagram\" style=\"width:70%;\"\u003e\u003cbr\u003e\n*DelegateMQ Layer Diagram*\n\nThe 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.\n\n# Getting Started\n\nTo build and run DelegateMQ, follow these simple steps. The library uses \u003ca href=\"https://www.cmake.org\"\u003eCMake\u003c/a\u003e to generate build files and supports Visual Studio, GCC, and Clang toolchains.\n\n1. Clone the repository.\n2. From the repository root, run the following CMake command:   \n   `cmake -B build .`\n3. Build and run the project within the `build` directory. \n\nSee [Sample Projects](docs/DETAILS.md#sample-projects) to build other project examples.\n\n# Documentation\n\n - See [Design Details](docs/DETAILS.md) for implementation design documentation and more examples.\n - See [Doxygen Documentation](https://endurodave.github.io/DelegateMQ/html/index.html) for source code documentation. \n - See [Unit Test Code Coverage](https://app.codecov.io/gh/endurodave/DelegateMQ) test results.\n\n# Motivation\n\nSystems 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.\n\nThe 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.\n\n# Features\n\nDelegateMQ at a glance. \n\n| Category | DelegateMQ |\n| --- | --- |\n| Purpose | Unify callable invocation across threads, processes, and networks |\n| Usages | Callbacks (synchronous and asynchronous), asynchronous API's, communication and data distribution, and more |\n| Library | Allows customizing data sharing between threads, processes, or processors |\n| Complexity | Lightweight and extensible through external library interfaces and full source code |\n| Threads | No internal threads. External configurable thread interface portable to any OS (`IThread`). |\n| Watchdog | Configurable timeout to detect and handle unresponsive threads. |\n| Multicast | Broadcast invoke anonymous callable targets onto multiple threads |\n| Message Priority | Asynchronous delegates support prioritization to ensure timely execution of critical messages |\n| Serialization | External configurable serialization data formats, such as MessagePack, RapidJSON, or custom encoding (`ISerializer`) |\n| Transport | External configurable transport, such as ZeroMQ, TCP, UDP, serial, data pipe or any custom transport (`ITransport`)  |\n| Timeouts and Retries | Provided by a communication library (e.g. ZeroMQ REQ-REP (Request-Reply)), TCP/IP stack, or custom solution (`ITransportMonitor`) |\n| Message Buffering | Remote delegate message buffering provided by a communication library (e.g. ZeroMQ) or custom solution within transport |\n| Dynamic Memory | Heap or DelegateMQ fixed-block allocator |\n| Debug Logging | Debug logging using spdlog C++ logging library |\n| Error Handling | Configurable for return error code, assert or exception |\n| Embedded Friendly | Yes. Any OS such as Windows, Linux and FreeRTOS. An OS is not required (i.e. \"super loop\"). |\n| Operation System | Any. Custom `IThread` implementation may be required. |\n| Language | C++17 or higher |\n\n# Alternative Implementations\n\nAlternative asynchronous implementations similar in concept to DelegateMQ, simpler, and less features.\n\n* \u003ca href=\"https://github.com/endurodave/AsyncCallback\"\u003eAsynchronous Callbacks in C++\u003c/a\u003e - A C++ asynchronous callback framework.\n* \u003ca href=\"https://github.com/endurodave/C_AsyncCallback\"\u003eAsynchronous Callbacks in C\u003c/a\u003e - A C language asynchronous callback framework.\n\n# Other Projects Using DelegateMQ\n\nRepositories utilizing the DelegateMQ library.\n\n* \u003ca href=\"https://github.com/endurodave/StateMachineWithModernDelegates\"\u003eC++ State Machine with Asynchronous Delegates\u003c/a\u003e - a framework combining a C++ state machine with the asynchronous delegate library.\n* \u003ca href=\"https://github.com/endurodave/AsyncStateMachine\"\u003eAsynchronous State Machine Design in C++\u003c/a\u003e - an asynchronous C++ state machine implemented using an asynchronous delegate library.\n* \u003ca href=\"https://github.com/endurodave/IntegrationTestFramework\"\u003eIntegration Test Framework using Google Test and Delegates\u003c/a\u003e - a multi-threaded C++ software integration test framework using Google Test and DelegateMQ libraries.\n* \u003ca href=\"https://github.com/endurodave/Async-SQLite\"\u003eAsynchronous SQLite API using C++ Delegates\u003c/a\u003e - an asynchronous SQLite wrapper implemented using an asynchronous delegate library.\n\n\n\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2FDelegateMQ","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fendurodave%2FDelegateMQ","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2FDelegateMQ/lists"}