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.git
In 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 install
Then:
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 build
Includes:
- 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_unseq
for scalable performance - ⛑️ Avoid
std::async
for 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.0
Pull requests welcome! Please open an issue for large changes before starting work.
For questions or collaborations, use GitHub Discussions.