Два асимметричных типа корутина -asymmetric_coroutine<>::push_typeиasymmetric_coroutine<>::pull_type— обеспечивает однонаправленную передачу данных.
asymmetric_coroutine<>::pull_typeпередает данные из другого контекста исполнения (== извлекается из). Параметр шаблона определяет тип передаваемого параметра. Конструкторasymmetric_coroutine<>::pull_typeвыполняет функциюcoroutine-function), принимая ссылку наasymmetric_coroutine<>::push_typeв качестве аргумента. Обоснованиеasymmetric_coroutine<>::pull_typeпередает управление исполнениемкорутинной функциии комплементарнойасимметричной_coroutine<>::push_typeсинтезируется библиотекой и передается как ссылка накорутин-функцию.
Этот вид корутина обеспечиваетasymmetric_coroutine<>::pull_type::operator(). Этот метод переключает только контекст; он не передает никаких данных.
asymmetric_coroutine<>::pull_typeпредоставляет итераторы вводаasymmetric_coroutine<>::pull_type:::iterator) иstd::begin()/std::end().Перегружены. Прирост-операция переключает контекст и передает данные.
boost::coroutines::asymmetric_coroutine<int>::pull_type source(
[&](boost::coroutines::asymmetric_coroutine<int>::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
В этом примереasymmetric_coroutine<>::pull_typeсоздается в контексте основного исполнения с использованием функции лямбда (==coroutine-function), которая вычисляет числа Фибоначчи в простомдля-петли.корутин-функциявыполняется в вновь созданном контексте исполнения, который управляется экземпляромasymmetric_coroutine<>::pull_type.asymmetric_coroutine<>::push_typeавтоматически генерируется библиотекой и передается в качестве ссылки на функцию лямбда. Каждый раз функция лямбда вызываетasymmetric_coroutine<>::push_type::operator()с другим числом Фибоначчи,asymmetric_coroutine<>::push_typeпереносит его обратно в основной контекст исполнения. Местное состояниекорутинной функциисохраняется и будет восстановлено при передаче управления исполнением обратнокорутинной функциидля вычисления следующего числа Фибоначчи. Потому чтоasymmetric_coroutine<>::pull_typeпредоставляет итераторы ввода иstd::begin()/std::end().перегружены,диапазон на основе-петля может использоваться для итерации по генерируемым числам Фибоначчи.
asymmetric_coroutine<>::push_typeпередает данные в другой контекст исполнения (== push-to). Параметр шаблона определяет тип передаваемого параметра. Конструкторasymmetric_coroutine<>::push_typeпринимает функциюкорутин-функция, принимая ссылку наасимметричную_корутин<>::pull_typeв качестве аргумента. В отличие отasymmetric_coroutine<>::pull_type, инстанцируяasymmetric_coroutine<>::push_typeне передает контроль исполнениякорутинной функции- вместо первого вызоваasymmetric_coroutine<>::push_type::operator()синтезирует комплементарныйasymmetric_coroutine<>::pull_typeи передает его в качестве ссылки накорутинная функция.
asymmetric_coroutine<>::push_typeинтерфейс не содержитget()-функции: вы не можете извлекать значения из другого контекста исполнения с этим видом корутина.
asymmetric_coroutine<>::push_typeобеспечивает выходные итераторыasymmetric_coroutine<>::push_type::iteratorиstd::begin()/std::end()перегружены. Прирост-операция переключает контекст и передает данные.
struct FinalEOL{
~FinalEOL(){
std::cout << std::endl;
}
};
const int num=5, width=15;
boost::coroutines::asymmetric_coroutine<std::string>::push_type writer(
[&](boost::coroutines::asymmetric_coroutine<std::string>::pull_type& in){
FinalEOL eol;
for (;;){
for(int i=0;i<num;++i){
if(!in) return;
std::cout << std::setw(width) << in.get();
in();
}
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(boost::begin(words),boost::end(words),boost::begin(writer));
output:
peas porridge hot peas porridge
cold peas porridge in the
pot nine days old
В этом примере aasymmetric_coroutine<>::push_typeсоздается в основном контексте исполнения, принимающем функцию лямбда (==корутин-функция), которая запрашивает строки и выкладывает их «номер» на каждой строке. Это свидетельствует об инверсии контроля, допускаемого корутинами. Без корутина функция полезности для выполнения одной и той же работы обязательно принимает каждое новое значение в качестве параметра функции, возвращаясь после обработки этого единственного значения. Эта функция будет зависеть от статической переменной состояния. Акорутинная функция, однако, может запрашивать каждое новое значение, как бы вызывая функцию, даже если ее вызывающий также передает значения, как если бы вызывая функцию.корутин-функциявыполняется в вновь созданном контексте исполнения, который управляется экземпляромasymmetric_coroutine<>::push_type. Основной контекст исполнения передает строкикорутинной функции, называяасимметричным_coroutine<>::push_type::operator(). Anасимметричным_coroutine<>::pull_typeэкземпляр автоматически генерируется библиотекой и передается в качестве ссылки на функцию лямбда.корутин-функцияполучает доступ к строкам, переданным из основного контекста исполнения, вызываяasymmetric_coroutine<>::pull_type::get()и выкладывает эти строки наstd::coutпо параметрам «номер» и «ширина». Местное состояниекорутинной функциисохраняется и будет восстановлено после передачи управления исполнением обратно.корутинная функция. Потому чтоасимметричная_корутина<>::push_typeобеспечивает выходные итераторы иstd::begin()/std::end()перегружены,std::copyалгоритм может быть использован для итерации по вектору, содержащему строки, и передачи их один за другим в корутин.
корутин-функциявозвращаетпустотуи принимает в качестве аргумента свой аналог-корутин, так что использование корутина, переданного в качестве аргументакорутин-функция, является единственным способом передачи данных и управления исполнением обратно вызывающему абоненту. Оба корутинных типа используют один и тот же шаблонный аргумент. Дляasymmetric_coroutine<>::pull_typeкорутин-функциявведена вasymmetric_coroutine<>::pull_typeстроительство. Дляasymmetric_coroutine<>::push_typeкорутин-функцияне вводится приasymmetric_coroutine<>::push_typeстроительство, но введенное первым вызовомasymmetric_coroutine<>::push_type::operator(). После возврата управления выполнением изкорутин-функциисостояние корутина может быть проверено черезasymmetric_coroutine<>::pull_type::operator boolreturn<true
>, если корутин все еще действителенкорутин-функцияне прекращена. Если первый параметр шаблона не является<void
>,<true
>также подразумевает, что значение данных доступно.
Для передачи данных изasymmetric_coroutine<>::pull_typeк основному контексту фреймворк синтезируетasymmetric_coroutine<>::push_typeсвязан сasymmetric_coroutine<>::pull_typeпример в главном контексте. Синтезированныйasymmetric_coroutine<>::push_typeпередается в качестве аргументакорутинной функции.coroutine-функциядолжна называть этоasymmetric_coroutine<>::push_type::operator()для передачи каждого значения данных обратно в основной контекст. В основном контекстеasymmetric_coroutine<>::pull_type::operator boolопределяет, является ли кодуин все еще действительным и доступно значение данных иликорутин-функцияпрекращенаasymmetric_coroutine<>::pull_typeнедействительна; значения данных нет. Доступ к передаваемому значению данных предоставляется поasymmetric_coroutine<>::pull_type::get().
boost::coroutines::asymmetric_coroutine<int>::pull_type source(
[&](boost::coroutines::asymmetric_coroutine<int>::push_type& sink){
sink(1);
sink(1);
sink(2);
sink(3);
sink(5);
sink(8);
});
while(source){
int ret=source.get();
source();
}
Для передачи данных вasymmetric_coroutine<>::push_typeиз основного контекста фреймворк синтезируетasymmetric_coroutine<>::pull_typeсвязан сasymmetric_coroutine<>::push_typeпример в главном контексте. Синтезированныйasymmetric_coroutine<>::pull_typeпередается в качестве аргументакорутинной функции. Основной контекст должен называть этоasymmetric_coroutine<>::push_type::operator().для передачи каждого значения данных вкорутинно-функцию. Доступ к передаваемому значению данных даетasymmetric_coroutine<>::pull_type::get().
boost::coroutines::asymmetric_coroutine<int>::push_type sink(
[&](boost::coroutines::asymmetric_coroutine<int>::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);
}
К параметрам, возвращаемым изкорутинной функции, можно получить доступ с помощьюasymmetric_coroutine<>::pull_type::get().
Разделение доступа к параметрам из функции переключателя контекста позволяет проверить, асимметричен лиasymmetric_coroutine<>::pull_typeдействителен после возвращения изasymmetric_coroutine<>::pull_type::operator(), напримерasymmetric_coroutine<>::pull_typeимеет значения икорутин-функцияне прекращена.
boost::coroutines::asymmetric_coroutine<boost::tuple<int,int>>::push_type sink(
[&](boost::coroutines::asymmetric_coroutine<boost::tuple<int,int>>::pull_type& source){
int x,y;
boost::tie(x,y)=source.get();
});
sink(boost::make_tuple(7,11));
Исключение, брошенное внутриasymmetric_coroutine<>::pull_type'scoroutine-functionперед его первым вызовомasymmetric_coroutine<>::push_type::operator()будет переброшенasymmetric_coroutine<>::pull_typeКонструктор. Послеasymmetric_coroutine<>::pull_type'scoroutine-functionпервый звонокasymmetric_coroutine<>::push_type::operator(), любое последующее исключение в том, чтокорутин-функциябудет повторно сброшенаасимметричным_coroutine<>::pull_type::operator().асимметричным_coroutine<>::pull_type::get()не бросает.
Исключение, брошенное внутриasymmetric_coroutine<>::push_type'sкорутин-функции, будет повторно брошеноasymmetric_coroutine<>::push_type::operator().
![[Important]](/img/important.png) |
Important |
Код, выполняемыйкорутинной функцией, не должен препятствовать распространениюдетали::forced_unwindисключения. Поглощение этого исключения приведет к провалу стека. Таким образом, любой код, который улавливает все исключения, должен повторно бросить любую ожидающуюдеталь::forced_unwindисключение. |
try {
} catch(const boost::coroutines::detail::forced_unwind&) {
throw;
} catch(...) {
}
![[Important]](/img/important.png) |
Important |
Не прыгайте изнутри блок-уловки, а затем повторно бросайте исключение в другой контекст исполнения. |
Иногда необходимо раскручивать стек незавершенного корутина, чтобы уничтожить локальные переменные стека, чтобы они могли высвобождать выделенные ресурсы (паттерн RAII). Аргумент<attributes
>корутинного конструктора указывает, должен ли деструктор разматывать стек (стек разматывается по умолчанию).
Stack unwinding предполагает следующие предварительные условия:
- Корутина нене-корутина
- Коротин не является полным
- Коротин не работает
- Коротин владеет стеком
После раскручивания завершаетсякорутина.
struct X {
X(){
std::cout<<"X()"<<std::endl;
}
~X(){
std::cout<<"~X()"<<std::endl;
}
};
{
boost::coroutines::asymmetric_coroutine<void>::push_type sink(
[&](boost::coroutines::asymmetric_coroutine<void>::pull_type& source){
X x;
for(int=0;;++i){
std::cout<<"fn(): "<<i<<std::endl;
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.Coroutineпредоставляет выходные и входные указатели, используяBoost.Range.asymmetric_coroutine<>::pull_typeможет использоваться с помощью входных устройств, использующихstd::begin()иstd::end().
int number=2,exponent=8;
boost::coroutines::asymmetric_coroutine< int >::pull_type source(
[&]( boost::coroutines::asymmetric_coroutine< int >::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
asymmetric_coroutine<>::pull_type::iterator::operator++()соответствуетasymmetric_coroutine<>::pull_type::operator();asymmetric_coroutine<>::pull_type::iterator::operator*()примерно соответствуетasymmetric_coroutine<>::pull_type::get(). Итератор, первоначально полученный изstd::begin()asymmetric_coroutine<>::pull_typeсравнивается с итератором, полученным изstd::end()того жеasymmetric_coroutine<>::pull_type, когда егоasymmetric_coroutine<>::pull_type::operator boolвернулся бы<false
>.
![[Note]](/img/note.png) |
Note |
Если<T >является только движущимся типом, тоасимметричен_coroutine< T>::pull_type::iteratorможет быть отменен только один раз, прежде чем он будет увеличен снова. |
Производители могут быть созданы изasymmetric_coroutine<>::push_type.
boost::coroutines::asymmetric_coroutine<int>::push_type sink(
[&](boost::coroutines::asymmetric_coroutine<int>::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(boost::begin(v),boost::end(v),boost::begin(sink));
asymmetric_coroutine<>::push_type::iterator::operator*()примерно соответствуетasymmetric_coroutine<>::push_type::operator(). Итератор, первоначально полученный изstd::begin()asymmetric_coroutine<>::push_typeсравнивается с итератором, полученным изstd::end()того жеasymmetric_coroutine<>::push_type, когда егоasymmetric_coroutine<>::push_type::operator boolвернется<false
>.
корутин-функциявыводится с простым заявлением о возврате, возвращающимся к процедуре вызова.asymmetric_coroutine<>::pull_type,asymmetric_coroutine<>::push_typeстановится полным, напримерasymmetric_coroutine<>::pull_type::operator bool,asymmetric_coroutine<>::push_type::operator boolвернется<false
>.
![[Important]](/img/important.png) |
Important |
После возвращения изкорутин-функциизавершенакорутина(не может быть возобновлена сasymmetric_coroutine<>::push_type::operator(),asymmetric_coroutine<>::pull_type::operator()). |