A lightweight, header-only C++20 Publish-Subscribe library with type-safe events, RAII-based unsubscription, and async support.
- ✅ Type-safe event publishing
- ✅ Support for multiple publishers and event types
- ✅ RAII-based unsubscription via
SubscriptionToken - ✅ Subscriber lifetime management
- ✅ Async dispatching via
std::async,std::execution, or oneTBB (if found) - ✅ Header-only, C++20
| OS | Compiler | Generator | Status |
|---|---|---|---|
| Ubuntu | g++, clang++ | Makefiles, Ninja | |
| Windows | MSVC | Visual Studio 17 | |
| macOS | clang++, g++ | Makefiles, Ninja |
🧪 All environments verified via GitHub Actions.
git clone https://github.com/cpp-for-everything/pubsub-lib.gitIn your CMakeLists.txt:
add_subdirectory(pubsub-lib)
target_link_libraries(my_app PRIVATE pubsub::pubsub)cmake -B build -S pubsub-lib -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build --target installThen:
find_package(pubsub REQUIRED)
target_link_libraries(my_app PRIVATE pubsub::pubsub)constexpr auto MyEvent = pubsub::Event<void(int)>();Or organize events:
struct MyEvents {
static constexpr auto Ping = pubsub::Event<void()>();
static constexpr auto Data = pubsub::Event<void(int)>();
};pubsub::Publisher pub;pub.subscribe<MyEvents::Ping>([] { std::cout << "Ping!\n"; });struct Listener {
void on_data(int x) { std::cout << "Got " << x << "\n"; }
} obj;
pub.subscribe<MyEvents::Data>(&obj, &Listener::on_data);class MySubscriber : public pubsub::Subscriber {
int total = 0;
public:
void on_data(int x) { total += x; }
void subscribe_to(pubsub::Publisher& pub) override {
store_token(pub.subscribe<MyEvents::Data>(this, &MySubscriber::on_data));
Subscriber::subscribe_to(pub);
}
void unsubscribe_from(pubsub::Publisher& pub) override {
pub.unsubscribe<MyEvents::Data>(this);
}
};pub.emit<MyEvents::Data>(123);pub.emit_thread_async<MyEvents::Data>(42);pub.emit_tbb_async<MyEvents::Data>(42);
⚠️ Make sure oneTBB is installed and discoverable by CMake.
pub.emit_async<MyEvents::Data>(std::execution::par_unseq, 42);cmake -B build -S .
cmake --build build
ctest --test-dir buildIncludes:
- Emission correctness
- Lifetime management
- Safe unsubscribing
- Async delivery checks
Benchmarks run using Google Benchmark with simulated heavy subscribers.
See benchmark/ for setup.
| Strategy | 1 sub | 10 subs | 100 subs | 500 subs | 1000 subs |
|---|---|---|---|---|---|
| Sync | 1.1 µs | 10 µs | 99 µs | 534 µs | 954 µs |
std::async |
74 µs | 682 µs | 7.2 ms | 42.5 ms | 109 ms |
std::execution::seq |
1.3 µs | 12.6 µs | 130 µs | 721 µs | 1.03 ms |
std::execution::par |
1.3 µs | 14.1 µs | 186 µs | 772 µs | 1.65 ms |
std::execution::unseq |
1.6 µs | 14.4 µs | 158 µs | 803 µs | 1.50 ms |
std::execution::par_unseq |
1.5 µs | 12 µs | 149 µs | 837 µs | 1.73 ms |
| oneTBB | 1.9 µs | 10.2 µs | 84 µs | 262 µs | 618 µs |
- ⚡ Use sync emit for low subscriber counts
- ♻ Use oneTBB or
par_unseqfor scalable performance - ⛑️ Avoid
std::asyncfor high fanout
If you use pubsub-lib in your research or projects, please cite the following publication:
Alex Tsvetanov and Ivan Stankov.
Modern C++ Publish/Subscribe Pattern: Design, Challenges, and Implementation.
In: Proceedings of the 60th International Scientific Conference on Information, Communication and Energy Systems and Technologies (ICEST 2025), Ohrid, North Macedonia, June 26–28, 2025.
https://github.com/cpp-for-everything/pubsub-lib
Apache License 2.0 — see LICENSE
All source files include:
// SPDX-License-Identifier: Apache-2.0Pull requests welcome! Please open an issue for large changes before starting work.
For questions or collaborations, use GitHub Discussions.
