С++ фьючерсы /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 С++.