![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Asymmetric coroutineBoost , Chapter 1. Coroutine2 , Coroutine
|
![]() |
Note |
---|---|
asymmetric_coroutine<>является типизированным значениемcoroutine. |
Coroutine<>::pull_typeпередает данные из другого контекста исполнения (== извлекается из). Параметр шаблона определяет тип передаваемого параметра. Конструкторcoroutine<>::pull_typeпринимает функциюcoroutine-function, принимая в качестве аргумента ссылку наcoroutine<>::push_type. Обоснованиеcoroutine<>::pull_typeпередает контроль исполненияcoroutine-функциии комплементарныйcoroutine<>::push_typeсинтезируется библиотекой и передается в качестве ссылки наcoroutine-функции.
Этот вид корутина обеспечиваеткорутин<>::pull_type::operator(). Этот метод переключает только контекст; он не передает никаких данных.
coroutine<>::pull_typeпредоставляет итераторы вводаcoroutine<>::pull_type:::iteratorиstd::begin()/std::end()Перегружены. Прирост-операция переключает контекст и передает данные.
typedef boost::coroutines2::coroutine<int> coro_t; coro_t::pull_type source( [&](coro_t::push_type& sink){ int first=1,second=1; sink(first); sink(second); for(int i=0;i<8;++i){ int third=first+second; first=second; second=third; sink(third); } }); for(auto i:source) std::cout << i << " "; output: 1 1 2 3 5 8 13 21 34 55
В этом примереcoroutine<>::pull_typeсоздается в контексте основного исполнения с использованием функции лямбда (==coroutine-function), которая вычисляет числа Фибоначчи в простомдля-петли.coroutine-функциявыполняется в вновь созданном контексте исполнения, который управляется экземпляромcoroutine<>::pull_type.coroutine<>::push_typeавтоматически генерируется библиотекой и передается как ссылка на функцию лямбда. Каждый раз, когда функция лямбда вызываетcoroutine<>::push_type::operator()с другим номером Фибоначчи,coroutine<>::push_typeпереносит его обратно в основной контекст исполнения. Местное состояниекорутинной функциисохраняется и будет восстановлено при передаче управления исполнением обратнокорутинной функциидля вычисления следующего числа Фибоначчи. Потому чтоcoroutine<>::pull_typeпредоставляет итераторы ввода иstd::begin()/std::end().перегружены,диапазон на основе-петля может использоваться для итерации по генерируемым числам Фибоначчи.
Coroutine<>::push_typeпередает данные в другой контекст исполнения (== push-to). Параметр шаблона определяет тип передаваемого параметра. Конструкторcoroutine<>::push_typeпринимает функциюcoroutine-function), принимая в качестве аргумента ссылку наcoroutine<>::pull_type. В отличие отcoroutine<>::pull_type, инстанцированиеcoroutine<>::push_typeне передает контроль исполненияcoroutine-функции— вместо первого вызоваcoroutine<>::push_type::operator()синтезирует комплементарныйкорутин<>::pull_typeи передает его в качестве ссылки накорутин-функцию.
Интерфейсcoroutine<>::push_typeне содержитget()-функции: вы не можете извлекать значения из другого контекста выполнения с этим видом coroutine.
coroutine<>::push_typeобеспечивает выходные итераторыcoroutine<>::push_type:::iteratorиstd::begin()/std::end()перегружены. Прирост-операция переключает контекст и передает данные.
typedef boost::coroutines2::coroutine<std::string> coro_t; struct FinalEOL{ ~FinalEOL(){ std::cout << std::endl; } }; const int num=5, width=15; coro_t::push_type writer( [&](coro_t::pull_type& in){ // finish the last line when we leave by whatever means FinalEOL eol; // pull values from upstream, lay them out 'num' to a line for (;;){ for(int i=0;i<num;++i){ // when we exhaust the input, stop if(!in) return; std::cout << std::setw(width) << in.get(); // now that we've handled this item, advance to next in(); } // after 'num' items, line break std::cout << std::endl; } }); std::vector<std::string> words{ "peas", "porridge", "hot", "peas", "porridge", "cold", "peas", "porridge", "in", "the", "pot", "nine", "days", "old" }; std::copy(begin(words),end(words),begin(writer)); output: peas porridge hot peas porridge cold peas porridge in the pot nine days old
В этом примереcoroutine<>::push_typeсоздается в основном контексте исполнения, принимающем функцию лямбда (==coroutine-function), которая запрашивает строки и выкладывает их «номер» на каждой строке. Это свидетельствует об инверсии контроля, допускаемого корутинами. Без корутина функция полезности для выполнения одной и той же работы обязательно принимает каждое новое значение в качестве параметра функции, возвращаясь после обработки этого единственного значения. Эта функция будет зависеть от статической переменной состояния. Акорутинная функция, однако, может запрашивать каждое новое значение, как бы вызывая функцию, даже если ее вызывающий также передает значения, как если бы вызывая функцию. Корутинная функциявыполняется в вновь созданном контексте исполнения, который управляется экземпляром.coroutine<>::push_type. Основной контекст исполнения передает строки.корутинная функцияпо телефонуcoroutine<>::push_type::operator(). Ancooutine<>::pull_typeэкземпляр автоматически генерируется библиотекой и передается в качестве ссылки на функцию лямбда.coroutine-functionполучает доступ к строкам, переданным из основного контекста выполнения, позвонивcoroutine<>::pull_type::get()и выкладывает эти строки наstd::coutпо параметрам «номер» и «ширина». Местное состояниекорутинной функциисохраняется и будет восстановлено после передачи управления исполнением обратно.корутинная функция. Потому чтоcoroutine<>::push_typeобеспечивает итераторы вывода иstd::begin()/std::end()перегружены, алгоритмstd::copyможет использоваться для итерации по вектору, содержащему строки, и передачи их по одному в корутин.
корутин-функциявозвращаетпустотуи принимает в качестве аргумента свой аналог-корутин, так что использование корутина, переданного в качестве аргументакорутин-функция, является единственным способом передачи данных и управления исполнением обратно вызывающему абоненту. Оба корутинных типа используют один и тот же шаблонный аргумент. Дляcoroutine<>::pull_typecoroutine-функциявведена вcoroutine<>::pull_typeконструкции. Дляcoroutine<>::push_typecoroutine-функцияне введена вcoroutine<>::push_typeстроительство, но введено первым вызовомкорутина<>::push_type::operator(). После возврата управления выполнением изкорутин-функциисостояние корутина может быть проверено черезкорутин<>::pull_type::operator bool, возвращающийся<true
>, если корутин все еще действителенкорутин-функцияне прекращена. Если первый параметр шаблона не является<void
>,<true
>также подразумевает, что значение данных доступно.
Для передачи данных изcoroutine<>::pull_typeв основной контекст фреймворк синтезируетcoroutine<>::push_type, связанный сcoroutine<>::pull_typeэкземпляром в главном контексте. Синтезированныйкорутин<>::push_typeпередается в качестве аргументакорутин-функция.coroutine-функциядолжна называть этоcoroutine<>::push_type::operator(), чтобы передать каждое значение данных обратно в основной контекст. В основном контекстеcoroutine<>::pull_type::operator boolопределяет, является ли coroutine все еще действительным и доступно значение данных илиcoroutine-functionпрекратила свою работуcoroutine<>::pull_typeявляется недействительной; значения данных не имеется. Доступ к передаваемому значению данных предоставляетсяcoroutine<>::pull_type::get().
typedef boost::coroutines2::coroutine<int> coro_t; coro_t::pull_type source( // constructor enters coroutine-function [&](coro_t::push_type& sink){ sink(1); // push {1} back to main-context sink(1); // push {1} back to main-context sink(2); // push {2} back to main-context sink(3); // push {3} back to main-context sink(5); // push {5} back to main-context sink(8); // push {8} back to main-context }); while(source){ // test if pull-coroutine is valid int ret=source.get(); // access data value source(); // context-switch to coroutine-function }
Для передачи данных вкорутин<>::push_typeиз основного контекста фреймворк синтезирует.coroutine<>::pull_type, связанный сcoroutine<>::push_typeв основном контексте. Синтезированныйкорутин<>::pull_typeпередается в качестве аргументакорутин-функция. Основной контекст должен называть этоcoroutine<>::push_type::operator(), чтобы передать каждое значение данных в.корутин-функция. Доступ к передаваемым данным предоставляется поcoroutine<>::pull_type::get().
typedef boost::coroutines2::coroutine<int> coro_t; coro_t::push_type sink( // constructor does NOT enter coroutine-function [&](coro_t::pull_type& source){ for (int i:source) { std::cout << i << " "; } }); std::vector<int> v{1,1,2,3,5,8,13,21,34,55}; for( int i:v){ sink(i); // push {i} to coroutine-function }
К параметрам, возвращаемым изкорутинной функции, можно получить доступ с помощьюкорутина<>::pull_type::get().
Разделение доступа к параметрам из функции переключателя контекста позволяет проверить, действителен ликорутин<>::pull_typeпосле возвращения из.coroutine<>::pull_type::operator(), напримерcoroutine<>::pull_typeимеет значения иcoroutine-функцияне прекращена.
typedef boost::coroutines2::coroutine<boost::tuple<int,int>> coro_t; coro_t::push_type sink( [&](coro_t::pull_type& source){ // access tuple {7,11}; x==7 y==1 int x,y; boost::tie(x,y)=source.get(); }); sink(boost::make_tuple(7,11));
Исключение, брошенное внутрикорутина<>::pull_type'sкорутин-функцияперед его первым вызовомcoroutine<>::push_type::operator()будет повторно брошен конструкторомcoroutine<>::pull_type. Послеcoroutine<>::pull_type's первого вызоваcoroutine<>::push_type::operator(), любое последующее исключение внутри этогоcoroutine-functionбудет повторно брошеноcoroutine<>::pull_type:::pull_type::get().
Исключение, брошенное внутрьcoroutine<>::push_type'scoroutine-функциябудет переброшенаcoroutine<>::push_type::operator().
![]() |
Important |
---|---|
Код, выполняемыйкорутинной функцией, не должен препятствовать распространениюдетали::forced_unwindисключения. Поглощение этого исключения приведет к провалу стека. Таким образом, любой код, который улавливает все исключения, должен повторно бросить любую ожидающуюдеталь::forced_unwindисключение. |
try { // code that might throw } catch(const boost::coroutines2::detail::forced_unwind&) { throw; } catch(...) { // possibly not re-throw pending exception }
![]() |
Important |
---|---|
Не прыгайте из ловушки и не выбрасывайте исключение в другом контексте исполнения. |
Иногда необходимо раскручивать стек незавершенного корутина, чтобы уничтожить локальные переменные стека, чтобы они могли высвобождать выделенные ресурсы (паттерн RAII). Аргумент<attributes
>корутинного конструктора указывает, должен ли деструктор разматывать стек (стек разматывается по умолчанию).
Stack unwinding предполагает следующие предварительные условия:
После раскручивания завершаетсякорутина.
struct X { X(){ std::cout<<"X()"<<std::endl; } ~X(){ std::cout<<"~X()"<<std::endl; } }; { typedef boost::coroutines2::coroutine<void>::push_type coro_t; coro_t::push_type sink( [&](coro_t::pull_type& source){ X x; for(int=0;;++i){ std::cout<<"fn(): "<<i<<std::endl; // transfer execution control back to main() source(); } }); sink(); sink(); sink(); sink(); sink(); std::cout<<"sink is complete: "<<std::boolalpha<<!sink<<"\n"; } output: X() fn(): 0 fn(): 1 fn(): 2 fn(): 3 fn(): 4 fn(): 5 sink is complete: false ~X()
Boost.Coroutine2обеспечивает выход-и-вход-итераторы с использованием __boost_range__.coroutine<>::pull_typeможет использоваться с помощью вход-итераторов с использованиемstd::begin()иstd::end().
typedef boost::coroutines2::coroutine< int > coro_t; int number=2,exponent=8; coro_t::pull_type source( [&](coro_t::push_type & sink){ int counter=0,result=1; while(counter++<exponent){ result=result*number; sink(result); } }); for (auto i:source) std::cout << i << " "; output: 2 4 8 16 32 64 128 256
coroutine<>::pull_type::iterator::operator++()соответствуетcoroutine<>::pull_type::operator();coroutine<>::pull_type::iterator::operator*()примерно соответствуетcoroutine<>::pull_type::get(). Итератор, первоначально полученный изstd::begin()coroutine<>::pull_type, сравнивается с итератором, полученным изstd::end()того жеcoroutine<>::pull_type, когда егоcoroutine<>::pull_type::operator boolвозвращался бы<false
>.
![]() |
Note |
---|---|
Если< |
Производители могут быть созданы изcoroutine<>::push_type.
typedef boost::coroutines2::coroutine<int> coro_t; coro_t::push_type sink( [&](coro_t::pull_type& source){ while(source){ std::cout << source.get() << " "; source(); } }); std::vector<int> v{1,1,2,3,5,8,13,21,34,55}; std::copy(begin(v),end(v),begin(sink));
coroutine<>::push_type::iterator::operator*()примерно соответствуетcoroutine<>::push_type::operator(). Итератор, первоначально полученный изstd::begin()изкорутина<>::push_typeсравнивается с итератором, полученным изstd::end()того жеcoroutine<>::push_type, когда егоcoroutine<>::push_type::operator boolвернется<false
>.
корутин-функциявыводится с простым заявлением о возврате, возвращающимся к рутине вызова.coroutine<>::pull_type,coroutine<>::push_typeстановится полным, напримерcoroutine<>::pull_type::operator bool,coroutine<>::push_type::operator boolвернется<false
>.
![]() |
Important |
---|---|
После возвращения изкорутин-функциизавершенакорутина(не может быть возобновлена скорутина<>::push_type::operator(),корутина<>::pull_type::operator(). |
Статья Asymmetric coroutine раздела Chapter 1. Coroutine2 Coroutine может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
реклама |