Реактивное программирование на C++: библиотеки и практика

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Реактивное программирование в C++: современный подход к обработке событий

Реактивное программирование (Reactive Programming) — это парадигма, ориентированная на асинхронные потоки данных и распространение изменений. В C++ эта концепция позволяет строить гибкие, отзывчивые и легко поддерживаемые приложения, особенно в сферах GUI, сетевого взаимодействия и обработки сигналов. В отличие от традиционного императивного подхода, где вы явно указываете последовательность действий, реактивное программирование описывает зависимости между данными: когда один элемент изменяется, все зависимые от него автоматически обновляются.

Основные концепции реактивного программирования

  • Observable (Наблюдаемый) — источник данных, который генерирует события.
  • Observer (Наблюдатель) — подписчик, реагирующий на события.
  • Subscription (Подписка) — связь между Observable и Observer.
  • Операторы — функции для трансформации, фильтрации и комбинирования потоков (map, filter, merge, switch_on_next).

В C++ реактивное программирование реализуется через библиотеки, предоставляющие эти абстракции. Рассмотрим три наиболее популярных инструмента.

1. RxCpp — библиотека Reactive Extensions для C++

RxCpp — это порт популярной модели ReactiveX (Rx) на C++. Она предоставляет мощный набор операторов для работы с асинхронными потоками. Библиотека использует современный C++17/20, шаблоны и лямбда-выражения.

Пример: простая подписка на поток чисел

#include <rxcpp/rx.hpp>
#include <iostream>

int main() {
    // Создаем наблюдаемый поток чисел
    auto observable = rxcpp::observable<>::range(1, 5);

    // Подписываемся
    observable.subscribe(
        [](int v) { std::cout << "Получено: " << v << std::endl; }, // on_next
        []() { std::cout << "Завершено!" << std::endl; }            // on_completed
    );

    return 0;
}

Пример: фильтрация и трансформация

#include <rxcpp/rx.hpp>
#include <iostream>

int main() {
    auto values = rxcpp::observable<>::range(1, 10)
        .filter([](int x) { return x % 2 == 0; })  // только четные
        .map([](int x) { return x * x; });         // возводим в квадрат

    values.subscribe([](int v) { std::cout << v << " "; });
    // Вывод: 4 16 36 64 100

    return 0;
}

Особенности RxCpp:

  • Поддержка многопоточности и планировщиков (schedulers).
  • Богатый набор операторов (merge, zip, combine_latest, debounce, throttle).
  • Интеграция с std::future и асинхронными операциями.
  • Совместимость с CMake и vcpkg.

2. Boost.Signals2 — сигналы и слоты без внешних зависимостей

Boost.Signals2 — это библиотека из состава Boost, реализующая механизм сигналов и слотов. Она является потокобезопасной эволюцией оригинальной Boost.Signals. Идеально подходит для событийно-ориентированной архитектуры, особенно в GUI и игровых движках.

Пример: простой сигнал с двумя слотами

#include <boost/signals2.hpp>
#include <iostream>

void hello() { std::cout << "Hello, "; }
void world() { std::cout << "world!" << std::endl; }

int main() {
    boost::signals2::signal<void()> sig;

    sig.connect(&hello);
    sig.connect(&world);

    sig();  // Вызов сигнала — вызовет оба слота
    // Вывод: Hello, world!

    return 0;
}

Пример: сигнал с параметрами и возвращаемым значением

#include <boost/signals2.hpp>
#include <iostream>
#include <string>

int main() {
    boost::signals2::signal<int(int, int)> sig;

    sig.connect([](int a, int b) { return a + b; });
    sig.connect([](int a, int b) { return a * b; });

    // Получаем агрегированный результат (последний слот по умолчанию)
    int result = sig(3, 4);
    std::cout << "Result: " << result << std::endl; //                    

Рекомендаций