-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 308c686
Showing
8 changed files
with
550 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.idea | ||
.vs | ||
!.gitkeep | ||
doc/ | ||
cmake-build-* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
cmake_minimum_required(VERSION 3.29) | ||
project(microbus) | ||
|
||
set(CMAKE_CXX_STANDARD 17) | ||
|
||
add_executable(microbus_examples src/examples.cpp) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Doxyfile 1.12.0 | ||
|
||
PROJECT_NAME = "microbus" | ||
|
||
OUTPUT_DIRECTORY = doc | ||
|
||
INPUT = src/microbus.hpp | ||
RECURSIVE = NO | ||
EXCLUDE_PATTERNS = */test/* | ||
EXTRACT_ALL = YES | ||
EXTRACT_PRIVATE = YES | ||
EXTRACT_STATIC = YES | ||
|
||
HAVE_DOT = YES | ||
DOT_MULTI_TARGETS = YES | ||
STRIP_FROM_PATH = | ||
|
||
WARN_IF_UNDOCUMENTED = YES | ||
WARN_IF_DOC_ERROR = YES | ||
|
||
GENERATE_HTML = YES | ||
GENERATE_LATEX = YES | ||
GENERATE_MAN = NO | ||
GENERATE_XML = NO | ||
GENERATE_RTF = NO | ||
|
||
PDF_HYPERLINKS = YES | ||
DOCUMENT_CLASS = "article" | ||
|
||
CLASS_GRAPH = YES | ||
COLLABORATION_GRAPH = YES | ||
CALL_GRAPH = YES | ||
CALLER_GRAPH = YES | ||
GRAPHICAL_HIERARCHY = YES |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 Igal Alkon | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# microbus - A simple event-bus and event-loop header-only library | ||
|
||
The `microbus` namespace provides a simple yet efficient event-bus and event-loop implementation in C++17. | ||
This library includes classes to manage subscriptions, event notifications, and asynchronous event processing. | ||
|
||
## Overview | ||
|
||
### Classes | ||
|
||
- **type_erased_handler**: An abstract base class for type-erased handlers. | ||
- **concrete_handler**: A concrete handler that binds a function to a given set of arguments. | ||
- **event_bus**: Manages event subscriptions and notifications. | ||
- **event_loop**: Processes asynchronous events. | ||
|
||
## Detailed Description | ||
|
||
### `type_erased_handler` | ||
|
||
This is a base class for handlers that erase type information. It provides a virtual `invoke` method which derived classes must implement. | ||
|
||
### `concrete_handler` | ||
|
||
A templated class that derives from `type_erased_handler`. It binds a specific function (handler) to a set of arguments. It overrides the `invoke` method to call the stored function with the provided arguments. | ||
|
||
### `event_bus` | ||
|
||
This class manages event subscriptions and notifications. It allows users to subscribe to events, unsubscribe, and trigger events. | ||
|
||
- **subscribe**: Allows a client to subscribe to an event with a specified handler. Returns a unique subscription ID. | ||
- **unsubscribe**: Unsubscribes a client from an event using the event name and subscription ID. | ||
- **trigger**: Triggers an event, calling all subscribed handlers with provided arguments. | ||
- **clear**: Clears all event subscriptions. | ||
|
||
### `event_loop` | ||
|
||
This class manages the processing of asynchronous events. It runs an internal thread to process events queued for execution. | ||
|
||
- **enqueue_event**: Enqueues an event for asynchronous processing. The event will be processed by calling the relevant handlers from the `event_bus`. | ||
- **wait_until_finished**: Blocks until all events in the queue are processed. | ||
- **stop**: Stops the event loop and joins the internal thread. | ||
|
||
## Key Features | ||
|
||
- **Thread-Safe**: Both `event_bus` and `event_loop` classes use mutexes to ensure thread-safety. | ||
- **Type Erasure**: Handlers are type-erased, allowing storage of various callable objects in a unified manner. | ||
- **Asynchronous Processing**: Events can be processed asynchronously using the `event_loop` class. | ||
- **Automatic Cleanup**: Event handlers are automatically cleaned up when unsubscribed or when the event bus is cleared. | ||
- **Flexible Subscription Management**: Supports multiple subscribers per event and manages them using unique subscription IDs. | ||
|
||
## Example Usage | ||
|
||
This example demonstrates subscribing to an event, | ||
triggering it synchronously and asynchronously, | ||
and then stopping the event loop. | ||
|
||
```cpp | ||
#include <iostream> | ||
#include <memory> | ||
#include "microbus.hpp" | ||
|
||
int main() { | ||
auto bus = std::make_shared<microbus::event_bus>(); | ||
microbus::event_loop loop; | ||
|
||
// Subscribe to event | ||
int subscription_id = bus->subscribe<int>("test_event", [](int value) { | ||
std::cout << "Received event with value: " << value << std::endl; | ||
}); | ||
|
||
// Trigger the event synchronously | ||
bus->trigger("test_event", 42); | ||
|
||
// Queue the event for asynchronous processing | ||
loop.enqueue_event(bus, "test_event", 100); | ||
|
||
// Wait for all events to be processed | ||
loop.wait_until_finished(); | ||
|
||
// Unsubscribe from the event | ||
bus->unsubscribe("test_event", subscription_id); | ||
|
||
// Stop the event loop | ||
loop.stop(); | ||
|
||
return 0; | ||
} | ||
``` |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
#include "microbus.hpp" | ||
|
||
#include <iostream> | ||
#include <thread> | ||
#include <chrono> | ||
#include <numeric> | ||
#include <vector> | ||
|
||
static int64_t factorial(int n); | ||
|
||
int main() { | ||
microbus::event_bus events; | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Simple event bus example | ||
|
||
// Define event handler types. | ||
using calc_event_handler = microbus::event_bus::event_handler<double, int>; | ||
using message_event_handler = microbus::event_bus::event_handler<std::string>; | ||
|
||
// Multiply PI event handler | ||
calc_event_handler multiply_by_pi = [](double value, int multiply_by) { | ||
std::cout << "Multiplying " << value << " by " << multiply_by << " is " << (value * multiply_by) << std::endl; | ||
}; | ||
int number_event_subscription_id = events.subscribe<double, int>("OnCalc", multiply_by_pi); | ||
|
||
// Message variable from main scope captured by lambda | ||
std::string greeting = "Hello, "; | ||
|
||
// Message event handler | ||
message_event_handler on_message = [&](const std::string &message) { | ||
std::cout << greeting << message << std::endl; | ||
}; | ||
int message_event_subscription_id = events.subscribe<std::string>("OnMessage", on_message); | ||
|
||
auto pie = 3.14159265; | ||
|
||
// Trigger events | ||
events.trigger("OnCalc", pie, 4); | ||
events.trigger("OnMessage", std::string("Joe")); | ||
|
||
// Unsubscribe the message event | ||
events.unsubscribe("OnMessage", message_event_subscription_id); | ||
|
||
// Trigger events after unsubscribing | ||
events.trigger("OnCalc", pie, 8); | ||
events.trigger("OnMessage", std::string("Jane")); // This won't produce output | ||
|
||
// Clear all events | ||
events.clear(); | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Smart pointer passed off-thread example | ||
|
||
using smart_pointer_event_handler = microbus::event_bus::event_handler<std::shared_ptr<std::string>>; | ||
|
||
// Message event handler | ||
smart_pointer_event_handler on_thread_message = [&](const std::shared_ptr<std::string>& message) { | ||
std::cout << "Received message: " << *message << std::endl; | ||
}; | ||
|
||
int smart_pointer_event_subscription_id = events.subscribe<std::shared_ptr<std::string>>("OnSmartPtrMessage", on_thread_message); | ||
|
||
// Function to trigger the event from another thread | ||
auto trigger_event_from_thread = [&events]() { | ||
auto message = std::make_shared<std::string>("Hello from another thread!"); | ||
events.trigger("OnSmartPtrMessage", message); | ||
}; | ||
|
||
// Create and launch a new thread | ||
std::thread event_thread(trigger_event_from_thread); | ||
|
||
// Wait for the event thread to finish | ||
event_thread.join(); | ||
|
||
// Clear event | ||
events.unsubscribe("OnSmartPtrMessage", smart_pointer_event_subscription_id); | ||
events.clear(); | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Shared event bus and event loop example | ||
|
||
std::shared_ptr<microbus::event_bus> shared_event_bus = std::make_shared<microbus::event_bus>(); // Ensure the use of shared_ptr | ||
microbus::event_loop event_loop; | ||
|
||
using factorial_event_handler = microbus::event_bus::event_handler<int>; | ||
|
||
factorial_event_handler compute_factorial = [](int number) { | ||
auto result = factorial(number); | ||
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | ||
std::cout << "Factorial of " << number << " is " << result << std::endl; | ||
}; | ||
|
||
shared_event_bus->subscribe<int>("OnFactorial", compute_factorial); | ||
|
||
std::vector<uint64_t> numbers_to_factorial1 = {15, 17, 19}; | ||
std::vector<uint64_t> numbers_to_factorial2 = {16, 18, 20}; | ||
|
||
for (uint64_t number : numbers_to_factorial1) { | ||
event_loop.enqueue_event(shared_event_bus, "OnFactorial", number); | ||
} | ||
|
||
for (uint64_t number : numbers_to_factorial2) { | ||
event_loop.enqueue_event(shared_event_bus, "OnFactorial", number); | ||
} | ||
|
||
// Wait until loop finishes handling all events | ||
event_loop.wait_until_finished(); | ||
event_loop.stop(); | ||
|
||
shared_event_bus->clear(); | ||
} | ||
|
||
static int64_t factorial(int n) { | ||
std::vector<int> numbers(n); | ||
std::iota(numbers.begin(), numbers.end(), 1); | ||
return std::accumulate(numbers.begin(), numbers.end(), 1LL, std::multiplies<>()); | ||
} |
Oops, something went wrong.