С++ фьючерсы /promises как javascript?

Я писал javascript, и одна из немногих вещей, которые мне нравятся в среде, - это способ использования promises/futures для создания обработчиков для асинхронных событий.

В С++ вам нужно вызывать .get в будущем, и он блокируется до тех пор, пока не появится результат будущего, но в Javascript вы можете написать .then(fn), и он вызывается функцией, когда результат будет готов. Критически он делает это в том же потоке, что и вызывающий, в дальнейшем, поэтому нет проблем с синхронизацией потоков, о которых нужно беспокоиться, по крайней мере, не таких, как в С++.

Я думаю в С++ что-то вроде -

auto fut = asyncImageLoader("cat.jpg");
fut.then([](Image img) { std::cout << "Image is now loaded\n" << image; });

Есть ли способ достичь этого в С++? Очевидно, что для обработки диспетчеризации обратных вызовов потребуется какая-то очередь событий и цикл событий. Возможно, я, вероятно, в конечном итоге напишу код, чтобы сделать большую часть этого, но хотел посмотреть, можно ли легко достичь цели, используя стандартные средства.

Ответ 1

Функция A .then для std::future была предложена для предстоящего стандарта С++ 17.

Повысить реализацию будущего (который соответствует текущему стандарту, но предоставляет дополнительные функции в качестве расширений) уже предоставляет части этой функциональности в более новых версиях (1.53 или новее)).

Для более устоявшегося решения взгляните на библиотеку Boost.Asio, что позволяет легко реализовать асинхронные потоки управления, предоставленные future.then, Концепция Asio немного сложнее, так как для доступа к центральному объекту io_service требуется доступ к асинхронным обратным вызовам и требуется ручное управление рабочими потоками. Но в принципе это очень хорошее совпадение с тем, что вы просили.

Ответ 2

В то время как then предлагается, вы можете реализовать свой собственный infix then с помощью указанной технологии оператора.

Создайте struct then_t {}; и static then_t then;. Теперь переопределите operator* слева и справа, чтобы std::future<bool> *then* lambda создавал std::async, ожидающий на future, и передает результат в lambda, затем возвращает возвращаемое значение лямбда.

Это требует большого внимания и внимания, так как вам нужно тщательно создавать копии, чтобы избежать оборванных ссылок, и обходиться с синтаксисом r и l, чтобы сделать его полностью эффективным.

Конечный синтаксис, который вы получаете, это:

aut fut = asyncLoader("cat.jpg");
fut *then* [&](Image img) { std::cout << "Image loaded: " << img; };

который близок к тому, что вы хотите.

Если вы действительно умны, вы даже можете его поддерживать:

aut fut = asyncLoader("cat.jpg");
fut *then* [=] { std::cout << "Image loaded: " << fut.get(); };

который избавляется от некоторых шаблонов и иногда будет полезен. Для этого требуется asyncLoader вернуть std::shared_future вместо future.

Ответ 3

Вы можете передать объект, например, реализующий класс Runnable методу "then" класса Future. Как только будущее закончит свою работу, вызовите метод "run" переданного объекта.

Ответ 4

Мне не нравится будущее С++, поэтому я написал библиотеки обещаний как javascript здесь https://github.com/xhawk18/promise-cpp

Defer newDelay(uint64_t timeout) {
return newPromise([timeout](Defer d) {
    setTimeout([d]() {
        d->resolve();
    }, timeout);
});

int main() {
    uv_loop_t *loop = uv_default_loop();

    newPromise([](Defer d) {
        setTimeout([d]() {
            printf("In timerout 1\n");
            d.resolve(893);
        }, 1000);
    }).then([](int vv) {
        printf("In then 1, vv = %d\n", vv);
        return newDelay(1000);
    }).then([]() {
        printf("In then 2\n");
        return newDelay(2000);
    }).then([]() {
        printf("In then 3\n");
        return newDelay(3000);
    }).then([]() {
        printf("In last then\n");
    });

    return uv_run(loop, UV_RUN_DEFAULT);
}

сравните с обещанием Javascript, просто -

  • Использовать newPromise вместо js new Promise
  • Использовать лямбда вместо функции js
  • Используйте d.resolve вместо js resolve
  • Используйте d.reject вместо js reject

Вы можете разрешать/отклонять с любыми параметрами и не нуждаться в заботе о шаблоне < > in С++.