Шаблон отложенного/обещания в C++

Недавно я обнаружил и влюбился в шаблон Deferred/Promise, используемый в jQuery. Он просто инкапсулирует так много асинхронных вариантов использования, включая замечательную цепочку и возможность фильтрации, что я не могу поверить, что так долго пропустил это.

Я только что закончил рефакторинг своего кода AS3, чтобы использовать отличную библиотеку CodeCatalyst/promise-as3 (https://github.com/CodeCatalyst/promise-as3), поэтому я начал думать о том, чтобы вернуться к моему коду на C++ и посмотреть, как я могу реализовать там шаблон.

Прежде чем я начал кодировать это сам, я проверил, было ли это сделано раньше, и обнаружил std::future/std::promise (и эквиваленты повышения), но они очень тяжелые (похоже, они используют настоящие потоки и т. д. и имеют тяжелый синтаксис шаблона) .

Итак, мой вопрос: существует ли легкая, чистая C++ реализация шаблона Deferred/Promise в стиле jQuery?

ссылки:


person Shane    schedule 09.07.2012    source источник
comment
Я не уверен, что вы подразумеваете под тяжелым синтаксисом шаблона. Вы просите версию без шаблона?   -  person Turix    schedule 09.07.2012
comment
Вы смотрели заголовок «будущее»? Проверьте это, а затем попробуйте выполнить отладчик, чтобы понять, что я имею в виду.   -  person Shane    schedule 09.07.2012


Ответы (7)


Извините, что играю в некроманта, но меня тоже очень интересовало использование промисов в стиле A+ в C++, и я потратил годы на разработку наилучшего способа его реализации. В конце концов мне это удалось, и вы можете увидеть мою реализацию здесь.

Использование довольно простое, но в нем активно используются шаблоны и метапрограммирование шаблонов. Вот пример:

Promise<int> promise;

promise.future().then([](int i){
    std::cout << "i = " << i << std::endl;
    return "foobar";
}).then([](const std::string& str){
    std::cout << "str = " << str << std::endl;
});

promise.resolve(10);

Это распечатает:

i = 10
str = foobar
person Oz.    schedule 15.08.2015
comment
Не могли бы вы вернуть обещание из предыдущего обратного вызова, чтобы связать обещание, возвращаемое методами внутри вызова then? - person Loïc Faure-Lacroix; 06.02.2017
comment
@ LoïcFaure-Lacroix Вы имеете в виду что-то вроде: Promise‹int› обещание; promise.future().then([](int i){ std::cout ‹‹ i = ‹‹ i ‹‹ std::endl; Promise‹std::string› promStr; someAsyncMethod([promStr]() { promStr.resolve(foobar); }) return promStr.future(); }).then([](const std::string& str){ std::cout ‹‹ str = ‹‹ str ‹‹ std::endl; }); обещание.разрешить(10); - person Oz.; 09.02.2017
comment
Комментарии не лучший вариант для фрагментов кода... но да. Вы можете вернуть промис из функции then-ed, и тип, к которому разрешается промис, соответствует тому, что должен ожидать следующий метод. - person Oz.; 09.02.2017
comment
Да, это то, что я имел в виду! Это круто. У меня не было большого опыта работы с С++ в течение многих лет, и я могу сказать. Оказывается, C++ начинает выглядеть как приличный язык программирования. - person Loïc Faure-Lacroix; 09.02.2017

Я не уверен, насколько легкое решение вам нужно, но std::async упрощает настройку future/promise много сочетается и позволяет вызывающей стороне решить, выполняется ли работа асинхронно другим потоком или с отложенным выполнением в том же потоке. В любом случае вызывающему объекту не нужно выполнять какое-либо явное управление потоком.

person juanchopanza    schedule 09.07.2012
comment
@Shane, тогда вы можете использовать std::async в режиме отложенного выполнения, но это будет блокировать ваш основной поток всякий раз, когда вы запрашиваете результат. - person juanchopanza; 09.07.2012
comment
Я посмотрел std::async, который выглядит близко. Похоже, что обратный вызов async запускается при возвращении Future get/wait. Это неправильный способ достижения того, чего я пытаюсь достичь, то есть я в основном хочу, чтобы куча обратных вызовов вызывалась, когда отложенный установлен (мной). Так работает jQuery. Упрощенно создается впечатление, что это фактически отложенный (заголовок списка) список с узлами (обещаниями). Когда отложенный установлен (успех/неудача), вызываются обратные вызовы (обещания). Видишь, что я имею в виду? - person Shane; 09.07.2012

Есть несколько причин, по которым я думаю, что то, о чем вы просите, почти невозможно на С++.

Прежде всего, чтобы использовать C++ 11 новый лямбда-синтаксис для объявления встроенных функций (эквивалент которого тривиален и очень легок в JavaScript), вам почти нужно использовать шаблоны для их использования.

Во-вторых, в отличие от JavaScript, здесь нет автоматического потока пользовательского интерфейса, в который вы могли бы припарковать свой таймер/очередь завершения, поэтому вам в значительной степени приходится прибегать к созданию пула потоков, которые следят за задачами, готовыми к выполнению своих задач. следующий шаг.

Что еще вы имеете в виду, когда говорите «чистый C++» и «легкий» (и неявно безпоточный)?

person sblom    schedule 09.07.2012
comment
Идея, что результат будет доступен каким-то другим способом в будущем, не должна зависеть от потока. Это может быть любая операция, которая может использовать или не использовать поток, например файловый ввод-вывод или ручной опрос или что-то еще. В конце концов, это просто связанный список обратных вызовов, которые запускаются при установке Deferred. - person Shane; 09.07.2012
comment
@shane, но что будет делать работу, за которую вы голосуете? Похоже, что отдельный процесс приемлем, верно? - person juanchopanza; 09.07.2012
comment
лямбда-ссылка заставила меня пнуть вас на 10 тысяч ;) - поздравляю - person slashmais; 09.07.2012
comment
Это может/должно быть даже, чтобы быть одним и тем же процессом. Подумайте: вы хотите, чтобы функция вызывалась, когда условие Future (триггер) было выполнено. Вы не знаете результат сейчас, поэтому вы возвращаете обещание (обратный вызов). Это обещание можно передавать и даже использовать в качестве фабрики для другого обещания, которые соединяются вместе. В какой-то момент условие было выполнено (через опрос, или таймер, или вообще что-то, в единственном потоке), и поэтому приложение устанавливает будущий результат. Это запускает каскад обратных вызовов обещаний. Надеюсь, это имеет немного больше смысла. - person Shane; 09.07.2012

Вы можете использовать совместную многозадачность. Я использую один в своем приложении. Единственная проблема, с которой я сталкиваюсь, заключается в том, что захваченные переменные уничтожаются, если я сохраняю лямбду внутри списка‹> и вызываю ее позже. Я до сих пор не нашел решения, но я совершенно уверен, что это должно быть возможно сделать.

person Martin    schedule 23.03.2014

Я знаю, что этот вопрос устарел, однако я полагаю, что стоит упомянуть также rili, который, вероятно, реализует именно то, что вы хотите.

Есть примеры и документация.

Как и lifewanted(liblw), он в значительной степени основан на шаблонах. Одной из основных особенностей этой библиотеки является реализация A+ Promises, включая Then (один и два параметра), Catch и finally.

Ссылки:

person Tomasz Frydrych    schedule 20.03.2017
comment
Он все еще существует? Домашняя страница дает мне 404 - person odiszapc; 25.01.2019
comment
Ага. Его перенесли на: gitlab.com/rilis/rili/promise — в прошлом году я понял, что набор моих библиотек постоянно растет, поэтому независимые части были перемещены в отдельные репозитории. - person Tomasz Frydrych; 29.01.2019

Вот еще одна библиотека обещаний C++, которая имитирует обещания JavaScript. Он разработан, чтобы быть легким, поэтому его можно использовать, например. асинхронное программное обеспечение с очень интенсивным вводом-выводом, и в то же время иметь столько же положительных качеств, что и обещания JS. Он уже много лет используется в очень популярном проекте, так что он проверен в бою: https://github.com/alxvasilev/cpp-promise

person Alexander Vassilev    schedule 09.01.2020

Просто используйте библиотеку Facebook Folly: https://github.com/facebook/folly/blob/master/folly/docs/Futures.md

Boost.Thread и C++17 (еще даже не поддерживающий комбинаторы) не предлагают такой большой функциональности, как Folly. В Folly вы даже можете получить успешные результаты вместо Try, как показано в ответе пользователя Oz. Вы можете ограничить количество потоков, используя исполнитель пула потоков (ранее он был частью Wangle, но теперь является частью Folly) или даже встроенный исполнитель. Без шаблонов в C++ далеко не уедешь, и они обеспечивают безопасность типов, что хорошо.

Имейте в виду, что вы должны использовать семантику перемещения для фьючерсов, но вы можете использовать folly::SharedPromise для создания нескольких фьючерсов из одного промиса.

Вы также можете просмотреть этот список: https://en.wikipedia.org/wiki/Futures_and_promises#List_of_implementations ("Нестандартные реализации фьючерсов на основе библиотек:").

person Baradé    schedule 27.09.2018