ãã®ããŒãžã¯C++20ã«æ¡çšãããèšèªæ©èœã®å€æŽã解説ããŠããŸãã
ã®ã¡ã®C++èŠæ Œã§ããã«å€æŽãããå Žåãããããé¢é£é ç®ãåç §ããŠãã ããã
æŠèŠ
åŠçéäžã§ã®ãµã¹ãã³ã(äžæ)ïŒã¬ãžã¥ãŒã (åé)ããµããŒãããäžè¬åããã颿°ãšããŠãã³ã«ãŒãã³ãå°å ¥ãããã
C++20æç¹ã§ã¯ãã³ã«ãŒãã³åäœã«é¢ããèšèªä»æ§ãšæ°ããŒã¯ãŒãco_return, co_await, co_yieldã«ããæ°ããæ§æãã³ã«ãŒãã³ã©ã€ãã©ãªå®è£
è
åãã®äœã¬ãã«ã©ã€ãã©ãª<coroutine>ã®ã¿ãèŠå®ãããã
// ã³ã«ãŒãã³iotaãå®çŸ©
my_generator iota(int end)
{
for (int n = 0; n < end; ++n) {
co_yield n;
}
}
// ã³ã«ãŒãã³ã®åŒã³åºã
auto g = iota(10);
for (int v: g) {
std::cout << v;
}
// "my_generator"ã¯ã©ã€ãã©ãªãæäŸããã¹ãã¯ã©ã¹ã
// åäœå¯èœãªãµã³ãã«ã³ãŒãå
šäœã¯åŸè¿°äŸãåç
§ã®ããšã
äžè¬çãªã¢ããªã±ãŒã·ã§ã³å®è£ è ããã®å©çšãæ³å®ããããžã§ãã¬ãŒã¿ãéåæã¿ã¹ã¯ã»éåæI/Oãšãã£ããã€ã¬ãã«ãªã³ã«ãŒãã³ã©ã€ãã©ãªã¯ãC++23以éã§ã®å°å ¥ã«ãããŠæ€èšãããŠããã
C++23ã§ã¯ãžã§ãã¬ãŒã¿ã³ã«ãŒãã³ãå®çŸãã<generator>ã远å ãããã
ç¹åŸŽ
C++ã³ã«ãŒãã³ã®ç¹åŸŽã¯æ¬¡ã®éãïŒ
- 颿°ããã³ã«ãŒãã³ãžã®æ¡åŒµ: åŸæ¥ãããã颿°(function)ã®åŒåºã(call)ãšåŸ©åž°(return)ã«å ããŠãã³ã«ãŒãã³(coroutine)ã§ã¯äžæ(suspend)ãšåé(resume)åäœããµããŒãããããŸãäžæç¶æ ã®ãŸãŸåéäžèŠãšãªã£ãã³ã«ãŒãã³ã«å¯ŸããŠã¯ããªãœãŒã¹ãªãŒã¯ãé²ãããæç€ºçã«ç Žæ£(destroy)ãè¡ããã
- 倿°ã® ã«ã¹ã¿ãã€ãºãã€ã³ã: ã³ã«ãŒãã³ã©ã€ãã©ãªå®è£ è åãã«ãã³ã«ãŒãã³åäœã®å¶åŸ¡ãå¯èœãšããã«ã¹ã¿ãã€ãºãã€ã³ããèŠå®ãããåŸè¿°ããPromiseãAwaitableãAwaiterãªã©ã
- 軜é㪠ã¹ã¿ãã¯ã¬ã¹(Stackless)ã³ã«ãŒãã³: ã³ã«ãŒãã³ã®äžæã¯å®è¡äžã³ã«ãŒãã³ã®ã¬ãã·ã«ã«ã»ã¹ã³ãŒãå
ã§ã®ã¿ã§èš±å¯ãããã³ã«ãŒãã³ãåŒã³åºãã颿°å
ã§ã¯äžææäœãè¡ããªããïŒC++ã³ã«ãŒãã³ã®å®çŸ©äžã
co_awaitãco_yieldãçšããŠäžæåŠçãèšè¿°ãããšã颿°ã§ã¯ãªãã³ã«ãŒãã³ãšã¿ãªããããïŒ - ã³ã«ãŒãã³æ¯ã® åçã¡ã¢ãªç¢ºä¿: ã³ã«ãŒãã³å®åŒæ°ã®ä¿æãé²è¡ç¶æ³ã管çãããããåçã¡ã¢ãªç¢ºä¿ãè¡ãããå¯èœæ§ãããããã ãäžå®ã®æ¡ä»¶ãæºããå Žåã«ã¯ãC++ã³ã³ãã€ã©æé©åã«ããåçã¡ã¢ãªç¢ºä¿ã¯çç¥ããããšæåŸ ã§ããã
- é察称(Asymmetric)ã»å¯Ÿç§°(Symmetric)ã³ã«ãŒãã³: äžæåŠçã«ããã³ã«ãŒãã³åéå
ãžå¶åŸ¡ãæ»ãé察称ã³ã«ãŒãã³ã®ã»ããæç€ºçã«å¥ã³ã«ãŒãã³ã®åéã«å¶åŸ¡ãç§»ã察称ã³ã«ãŒãã³ããµããŒããããåŸ
æ©åäœãã«ã¹ã¿ãã€ãºããAwaiterãªããžã§ã¯ã
await_suspendã«ãŠå¶åŸ¡ããã - ã¹ã¬ãã(thread)ãšã®çŽäº€: ããã¹ã¬ããäžã§å®è¡ãããã³ã«ãŒãã³ãäžæãããã®åŸã«å¥ã¹ã¬ããããåã³ã«ãŒãã³ãåéãããããšãã§ããããã ãã¹ã¬ããããŒã«ã«ã¹ãã¬ãŒãžãšçµåãã«ã¯æ³šæãå¿ èŠã
åäœæŠç¥
C++ã³ã«ãŒãã³åäœçè§£ã®å©ããšãªããããããã§ã¯çްéšãçç¥ãã説æãè¡ãã ã³ã«ãŒãã³å®è¡ã®åºæ¬åäœã¯ãããããäžèšã®ããã«èª¬æãããïŒ
- åŒåºã: éåžžã®é¢æ°ãšåæ§ã«ãã«ãã³(
())ãçšãã颿°åŒã³åºãæ§æãçšãããã³ã«ãŒãã³ã«å¯Ÿå¿ãããŠãŒã¶å®çŸ©Promiseãªããžã§ã¯ããèªåçã«çæããããããããããã³ã«ãŒãã³ãã³ãã«ãååŸããã - äžæ: ã³ã«ãŒãã³æ¬äœã«ãŠ
co_yieldåŒãŸãã¯co_awaitåŒãèšè¿°ãããäžæãããã³ã«ãŒãã³ããåŒåºå ãžã¯ãã³ã«ãŒãã³ãã³ãã«ãå å ããã³ã«ãŒãã³æ»ãå€åãªããžã§ã¯ããè¿ãã - åé: ã³ã«ãŒãã³äžæã«ããè¿ãããæ»ãå€åãªããžã§ã¯ããä»ããŠãã³ã«ãŒãã³ãã³ãã«ã®åé颿°
resumeãåŒã³åºãã - 埩垰: ã³ã«ãŒãã³æ¬äœã«ãŠ
co_returnæãèšè¿°ããŸãã¯ã³ã«ãŒãã³æ¬äœçµç«¯ãŸã§å°éãããå šãŠã®ããŒã«ã«å€æ°ãšPromiseãªããžã§ã¯ãã¯ç Žæ£ãããã
ãã®4çš®é¡ã®åºæ¬åäœã«å¯ŸããŠã次ã®ã«ã¹ã¿ãã€ãºãã€ã³ããæäŸããããã«ãã³å ã¯ã³ã«ãŒãã³ã©ã€ãã©ãªãå®è£ ãã¹ãã«ã¹ã¿ãã€ãºãã€ã³ãåïŒ
- ã³ã«ãŒãã³åŒåºãçŽåŸã®åäœ: åæãµã¹ãã³ããã€ã³ã(
initial_suspend)ã«ãŠãã³ã«ãŒãã³æ¬äœã®éå§åã«äžæããŠæ»ãå€åãªããžã§ã¯ããè¿ããããã®ãŸãŸã³ã«ãŒãã³æ¬äœãå®è¡ç¶ç¶ããããå¶åŸ¡ããã - ã³ã«ãŒãã³åŸ©åž°çŽåã®åäœ: æçµãµã¹ãã³ããã€ã³ã(
final_suspend)ã«ãŠãã³ã«ãŒãã³ãæåŸã«äžæããŠæ»ãå€åãªããžã§ã¯ããè¿ããããã®ãŸãŸã³ã«ãŒãã³ã«é¢ãããªãœãŒã¹ãç Žæ£ããããå¶åŸ¡ãããåè ãéžæããå ŽåããªãœãŒã¹ãªãŒã¯ãé²ãããã³ã«ãŒãã³ãã³ãã«ã®ç Žæ£é¢æ°destroyåŒåºããå¿ èŠãšãªãã - å€ã䌎ãã³ã«ãŒãã³äžæ:
co_yieldåŒã«ãããã³ã«ãŒãã³ãäžæãããšåæã«åŒåºå ãžå€ãè¿ã(yield_value)ã - å€ã䌎ãã³ã«ãŒãã³åŸ©åž°:
co_returnæã«ãããåŒåºå ãžå€ãè¿ã(return_value)ã - ã³ã«ãŒãã³äžæïŒåéå¶åŸ¡:
co_awaitåŒã«ãããã³ã«ãŒãã³äžæãšåéã«é¢ããæ¯ãèãã詳现ã«å¶åŸ¡ãã(await_transform,operator co_await)ã- ã³ã«ãŒãã³äžæã®æ¡ä»¶:
co_awaitåŒã«å¯ŸããŠãã³ã«ãŒãã³ãäžæãããåŠãã倿ãã(await_ready)ã - ã³ã«ãŒãã³äžæçŽåã®åäœ:
co_awaitåŒã«å¯ŸããŠãã³ã«ãŒãã³ãäžæããçŽåã®åäœãå¶åŸ¡ãã(await_suspend)ã - ã³ã«ãŒãã³åéçŽåŸã®åäœ:
co_awaitåŒã«å¯ŸããŠãã³ã«ãŒãã³ãåéãããçŽåŸã®åäœãå¶åŸ¡ãã(await_resume)ã
- ã³ã«ãŒãã³äžæã®æ¡ä»¶:
ããã°ã©ããèšè¿°ããã³ã«ãŒãã³ã¯ãã³ã³ãã€ã«æã«ãœãŒã¹ã³ãŒã倿ãè¡ããããšè§£éã§ããã ïŒåŸæ¥ã®C++仿§ç¯å²ã§ã¯ã³ã«ãŒãã³åäœãæ£ç¢ºã«è¡šçŸã§ããªããããäžèšã¯ãããŸã§ãç䌌çãªã³ãŒããšãªãïŒïŒ
// ããã°ã©ããèšè¿°ããã³ã«ãŒãã³
my_generator iota(int end)
{
for (int n = 0; n < end; ++n) {
co_yield n;
}
}
// C++ã³ã³ãã€ã©ã«ããå±éãããã³ãŒã
my_generator iota(int end)
{
// ã³ã«ãŒãã³ã«å¯Ÿå¿ããPromiseãªããžã§ã¯ããåæå
my_generator::promise_type promise;
// æ»ãå€åãªããžã§ã¯ãã®åæå
my_generator result = promise.get_return_object();
// ã³ã«ãŒãã³ãã³ãã«ãget_return_objectå
ã§ååŸããresultã¡ã³ãã§ä¿æããã
// çæããresultãªããžã§ã¯ãã¯ãååã®ã³ã«ãŒãã³äžææã«åŒåºå
ãžè¿ãããã
// æ¬äŸã§ã¯å
šãŠ co_await std::suspend_always{} çžåœã®ããã
// 以éã®co_awaitåŒ(â
ç®æ)ã«ãããŠã³ã«ãŒãã³ã¯äžæïŒåéãããã
// åæãµã¹ãã³ããã€ã³ã
co_await promise.initial_suspend(); //â
// ã³ã«ãŒãã³æ¬äœéš
{
for (int n = 0; n < end; ++n) {
// co_yieldåŒã¯äžèšco_awaitåŒã«å±éããã
co_await promise.yield_value(n); //â
}
}
promise.return_void();
// æçµãµã¹ãã³ããã€ã³ã
co_await promise.final_suspend(); //â
// æ¬äŸã§ã¯æçµãµã¹ãã³ããã€ã³ãã§ã³ã«ãŒãã³ãäžæãããããããã«ã¯å¶åŸ¡ãå°éããªãã
// åŒåºåŽã§æ»ãå€ãªããžã§ã¯ããç Žæ£ãããšããã¹ãã©ã¯ã¿çµç±ã§æ¬ã³ã«ãŒãã³ã¯ç Žæ£ãããã
}
仿§
ã³ã«ãŒãã³å®çŸ©
C++ã«ãããã³ã«ãŒãã³ã¯ã颿°ã®äžçš®ãšããŠå®çŸ©ãããã
颿°æ¬äœã«æ°ããŒã¯ãŒãco_await(AwaitåŒ), co_yield(YieldåŒ), co_returnã®ãããããå«ãŸãããšãããã®é¢æ°ã¯ã³ã«ãŒãã³ãšãªãã
ã€ãŸããæ»ãå€åãåŒæ°ãªã¹ããªã©ã®ã·ã°ããã£ããã³ã«ãŒãã³ïŒé¢æ°ãåºå¥ããããšã¯ã§ããªãã
ã³ã«ãŒãã³ã®åŒæ°å®£èšãªã¹ãã¯Cèšèªç±æ¥ã®å¯å€åŒæ°ãªã¹ã(...)ãå«ãã§ã¯ãªããªãããå¯å€åŒæ°ãã³ãã¬ãŒãã®ãã©ã¡ãŒã¿ããã¯(...)ã¯å©çšã§ããã
task<int> f();
task<void> g1() {
int i = co_await f();
std::cout << "f() => " << i << std::endl;
}
template <typename... Args>
task<void> g2(Args&&...) { // OK, "..."ã¯ããã¯å±é
int i = co_await f();
std::cout << "f() => " << i << std::endl;
}
task<void> g3(int a, ...) { // ãšã©ãŒ: å¯å€åŒæ°ãªã¹ãã¯èš±å¯ãããªã
int i = co_await f();
std::cout << "f() => " << i << std::endl;
}
ããã°ã©ã ãšã³ããªãã€ã³ãã®main颿°ãconstexpr颿°ãæ»ãå€åããã¬ãŒã¹ãã«ã(auto)ã§å®£èšããã颿°ãã¯ã©ã¹åã®ã³ã³ã¹ãã©ã¯ã¿ãšãã¹ãã©ã¯ã¿ã¯ãã³ã«ãŒãã³ãšããŠå®çŸ©ã§ããªãã
Promiseåãšã³ã«ãŒãã³åäœä»æ§
ã³ã«ãŒãã³ã®Promiseåã¯ãã³ã«ãŒãã³ã®æ»ãå€åRãšåŒæ°ãªã¹ãP1, P2, ..., Pnããæ±ºå®ãããã¯ã©ã¹åã§ããã
- ããã©ã«ãåäœã§ã¯
R::promise_typeãPromiseåãšãªãã - ãŠãŒã¶ããã°ã©ã äžã§
std::coroutine_traitsãã¬ã€ããç¹æ®åããå Žåã¯ãcoroutine_traits<R, P1, P2, ..., Pn>::promise_typeãPromiseåãšãªãã - ã³ã«ãŒãã³ãã¯ã©ã¹ã®ééçã¡ã³ãã®å Žåã
P1ã¯æé»ã®ãªããžã§ã¯ããã©ã¡ãŒã¿(*thisã®å)ãšãªãã
ã³ã«ãŒãã³ã¯ããã®æ¬äœ function-body ãäžèšã®éã眮ãæãããããã®ããã«åäœããïŒ
{
promise-type promise promise-constructor-arguments ;
try {
co_await promise.initial_suspend() ;
function-body
} catch ( ... ) {
if (! initial-await-resume-called )
throw ;
promise.unhandled_exception() ;
}
final-suspend :
co_await promise.final_suspend() ;
}
initial_suspendåŒã³åºããå«ãAwaitåŒã¯ãåæãµã¹ãã³ããã€ã³ããšãªããfinal_suspendåŒã³åºããå«ãAwaitåŒã¯ãæçµãµã¹ãã³ããã€ã³ããšãªãã- initial-await-resume-called ã¯
falseã§åæåãããåæãµã¹ãã³ããã€ã³ãã®åŒ await-resume ãè©äŸ¡ãããçŽåã«trueãèšå®ãããã - promise-type ã¯Promiseåã衚ãã
- 説æçšã®å€æ°å promise ã¯ãã³ã«ãŒãã³ã®Promiseãªããžã§ã¯ãã衚ãã
- ã©ãã« final-suspend ã¯èª¬æã®ããã«ã®ã¿å®çŸ©ãããã
- promise-constructor-arguments ã¯æ¬¡ã®éãã«æ±ºå®ããã:
- åŒæ°ãªã¹ã
Piã®å·ŠèŸºå€ãpiãšãããã³ã«ãŒãã³ãééçã¡ã³ãã®å Žåãp1ã¯*thisã衚ãp(i+1)ã¯içªç®ã®é¢æ°ãã©ã¡ãŒã¿ã衚ãã - 巊蟺å€
p1...pnã®å®åŒæ°ãªã¹ããçšããŠãPromiseã³ã³ã¹ãã©ã¯ã¿åŒã³åºãã®ãªãŒããŒããŒã解決ã詊ã¿ãã - é©åããã³ã³ã¹ãã©ã¯ã¿ãèŠã€ãã£ãå Žåã¯ãpromise-constructor-arguments ã¯
(p1, ..., pn)ãšãªããèŠã€ãããªãã£ãå Žåãpromise-constructor-arguments ã¯ç©ºã®ãªã¹ããšãªãã
- åŒæ°ãªã¹ã
Promiseåã®ã¹ã³ãŒãã«ãããŠãé修食ãªreturn_voidããã³return_valueã®æ¢çŽ¢ãè¡ããããäž¡æ¹ãèŠã€ãã£ãå Žåãããã°ã©ã ã¯äžé©æ Œãšãªãã
- C++29 : ãã®å¶éã¯åé€ãããäž¡æ¹ã宣èšã§ããããã«ãªã£ã
ã³ã«ãŒãã³åŒã³åºãã®glvalueçµæãŸãã¯prvalueçµæãªããžã§ã¯ããåæåããããã«ãåŒ promise.get_return_object()ã䜿ãããã
get_return_objectåŒã³åºãã¯é«ã
1åã§ãããinitial_suspendåŒã³åºããããåã«é åºä»ããããã
äžæç¶æ ã«ããã³ã«ãŒãã³ã¯ããã®ã³ã«ãŒãã³ãæãã³ã«ãŒãã³ãã³ãã«ã®åéã¡ã³ã颿°åŒã³åºãã«ãã£ãŠãç¶ç¶å®è¡ãåéã§ããã åéã¡ã³ã颿°ãåŒã³åºãã颿°ã¯ãåéå (resumer)ãšåŒã°ããã äžæç¶æ ã«ãªãã³ã«ãŒãã³ã«å¯Ÿããåéã¡ã³ã颿°åŒã³åºãã¯ãæªå®çŸ©ã®åäœãããããã
åŠçç³»ã¯ã³ã«ãŒãã³ã®ããã«è¿œå ã®ã¡ã¢ãªé åã確ä¿ããå¿
èŠããããããããªãã
ãã®ã¡ã¢ãªé åã¯ã³ã«ãŒãã³ã»ã¹ããŒããšããŠç¥ãããéé
åçã®ã¡ã¢ãªç¢ºä¿é¢æ°(operator new)ã«ãã£ãŠç¢ºä¿ãããã
ã¡ã¢ãªç¢ºä¿é¢æ°ã¯Promiseåã®ã¹ã³ãŒãã§ååæ¢çŽ¢ãè¡ãããã
ååæ¢çŽ¢ã«å€±æããå Žåã¯ãã°ããŒãã«ã¹ã³ãŒãã§æ¢çŽ¢ãè¡ãããã
ååæ¢çŽ¢ãPromiseåã®ã¹ã³ãŒãã§ç¢ºä¿é¢æ°ãèŠã€ããå Žåã¯ãå®åŒæ°ãªã¹ããçšããŠé¢æ°åŒã³åºãã®ãªãŒããŒããŒã解決ãè¡ãããã
第1åŒæ°ã¯std::size_tåã§ãããèŠæ±ã¡ã¢ãªãµã€ãºã®åèšå€ãšãªããç¶ãå®åŒæ°ã¯å·ŠèŸºå€p1...pnãšãªãã
é©åãã颿°ãèŠã€ãããªãã£ãå Žåãstd::size_tåã®èŠæ±ã¡ã¢ãªãµã€ãºåèšå€ã®ã¿ã§å床ãªãŒããŒããŒã解決ãè¡ãããã
Promiseåã®ã¹ã³ãŒãã«ãããŠãé修食ãªget_return_object_on_allocation_failureã®æ¢çŽ¢ãè¡ãããã
äœããã®å®£èšãèŠã€ãã£ãå Žåãã°ããŒãã«ãª::operator new(size_t nothrow_t)圢åŒã®ç¢ºä¿é¢æ°ãéžæãããã¡ã¢ãªé å確ä¿ã«å€±æãããšãã³ã«ãŒãã³ã»ã¹ããŒãçšã¡ã¢ãªé åååŸã®ããã®ç¢ºä¿é¢æ°åŒã³åºãçµæã¯nullptrãè¿ããšæ³å®ãããã
ãã®ã±ãŒã¹ã«ããã確ä¿é¢æ°ã¯ãäŸå€ãæããªãnoexceptæå®ãããã¹ãã§ããã
ã¡ã¢ãªç¢ºä¿é¢æ°ãnullptrãè¿ããå Žåãã³ã«ãŒãã³ã¯ãã®åŒã³åºãå
ã«å¶åŸ¡ãæ»ããæ»ãå€ã¯T::get_return_object_on_allocation_failure()åŒã³åºãã«ããååŸããã
ããã§Tã¯Promiseåã衚ãã
#include <iostream>
#include <coroutine>
#include <new> // std::nothrow
#include <exception> // std::terminate()
// ã¡ã¢ãªç¢ºä¿ãå¿
èŠãšãªã£ããšãã¯::operator new(size_t, nothrow_t)ã䜿ããã
struct generator {
struct promise_type;
using handle = std::coroutine_handle<promise_type>;
struct promise_type {
int current_value;
static auto get_return_object_on_allocation_failure() { return generator{nullptr}; }
auto get_return_object() { return generator{handle::from_promise(*this)}; }
auto initial_suspend() { return std::suspend_always{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
auto yield_value(int value) {
current_value = value;
return std::suspend_always{};
}
};
bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; }
int current_value() { return coro.promise().current_value; }
generator(generator const&) = delete;
generator(generator && rhs) : coro(rhs.coro) { rhs.coro = nullptr; }
~generator() { if (coro) coro.destroy(); }
private:
generator(handle h) : coro(h) {}
handle coro;
};
generator f() { co_yield 1; co_yield 2; }
int main() {
auto g = f();
while (g.move_next()) std::cout << g.current_value() << std::endl;
}
ã³ã«ãŒãã³ã®çµç«¯ãŸã§å¶åŸ¡ãå°éããŸãã¯ã³ã«ãŒãã³ãæãã³ã«ãŒãã³ãã³ãã«ã®destroyã¡ã³ã颿°ãåŒã³åºããããšãã³ã«ãŒãã³ã»ã¹ããŒãã¯ç Žæ£ãããã
ã¡ã¢ãªè§£æŸé¢æ°ã¯Promiseåã®ã¹ã³ãŒãã§ååæ¢çŽ¢ãè¡ãããã
ååæ¢çŽ¢ã«å€±æããå Žåã¯ãã°ããŒãã«ã¹ã³ãŒãã§æ¢çŽ¢ãè¡ãããã
è§£æŸé¢æ°ã®æ¢çŽ¢ãããã€ã³ã¿ãã©ã¡ãŒã¿ã®ã¿ã®éåžžã®è§£æŸé¢æ°ãšããã€ã³ã¿ãšãµã€ãºããã©ã¡ãŒã¿ã«ãšãéåžžã®è§£æŸé¢æ°ã®äž¡æ¹ãèŠã€ããå Žåã2åã®ãã©ã¡ãŒã¿ããšãè§£æŸé¢æ°ãéžæãããã
ããã§ãªããã°ã1åã®ãã©ã¡ãŒã¿ããšãè§£æŸé¢æ°ãéžæãããã
éåžžã®è§£æŸé¢æ°ãèŠã€ãããªããã°ãããã°ã©ã ã¯äžé©æ Œãšãªãã
éžæãããè§£æŸé¢æ°ã®åŒã³åºãã§ã¯ããã®ç¬¬1å®åŒæ°ã«è§£æŸãã¹ãã¡ã¢ãªãããã¯ã®ã¢ãã¬ã¹ãæž¡ãããã
è§£æŸé¢æ°ã®ãã©ã¡ãŒã¿ã«std::size_tã䜿ãããå Žåããã®å®åŒæ°ãšããŠã¡ã¢ãªãããã¯ã®ãµã€ãºãæž¡ãããã
ã³ã«ãŒãã³ãåŒã³åºããããšãããã©ã¡ãŒã¿åæåãè¡ãããã®ã¡ãåã³ã«ãŒãã³ãã©ã¡ãŒã¿ã®ã³ããŒãäœæãããã
åcv Tããã€ãã©ã¡ãŒã¿ã«ãããŠããã®ã³ããŒã¯ãã©ã¡ãŒã¿ãåç
§ããTåã®xvalueã§çŽæ¥åæåãããèªåèšæ¶åæéããã€cv Tåã®å€æ°ãšãªãã
åãã©ã¡ãŒã¿ã®ã³ããŒã®åæåãšç Žæ£ã¯ãåŒã³åºãããã³ã«ãŒãã³ã®ã³ã³ããã¹ãã§è¡ãããã
ãã©ã¡ãŒã¿ã®ã³ããŒã®åæåã¯ãã³ã«ãŒãã³Promiseã³ã³ã¹ãã©ã¯ã¿ã®åŒã³åºãããåã«é åºä»ããããããããã¯äºãã«é決å®é ã§é åºä»ããããã
ãã©ã¡ãŒã¿ã®ã³ããŒã®çåæéã¯ããã©ã¡ãŒã¿Promiseãªããžã§ã¯ãã®çµäºçŽåŸã§çµäºããã
ïŒã³ã«ãŒãã³ãåç
§æž¡ãã®ãã©ã¡ãŒã¿ãæã€å Žåããã®ãã©ã¡ãŒã¿ã«ããåç
§ããããšã³ãã£ãã£çåæéçµäºåŸã®ã³ã«ãŒãã³åéã¯æªå®çŸ©åäœãåŒãèµ·ããããããïŒ
åŒ promise.unhandled_exception()ã®è©äŸ¡ãäŸå€ã§çµäºããå Žåãã³ã«ãŒãã³ã¯æçµãµã¹ãã³ããã€ã³ãã§äžæãããšã¿ãªãããã
åŒ co_await promise.final_suspend() ã¯äŸå€éåºããŠã¯ãªããªãã
AwaitåŒ
co_awaitåŒã¯ããã®ãªãã©ã³ãåŒã§è¡šãããèšç®ã®å®äºãåŸ
æ©ããŠããããã ãã³ã«ãŒãã³è©äŸ¡ããµã¹ãã³ã(äžæ)ããããã«çšããã
co_await cast-expression
AwaitåŒã¯ãã³ã«ãŒãã³æ¬äœè€åæã®å
åŽïŒãã€tryïœcatchæ§æã®catchç¯ã®å€åŽïŒã«ãããŠæœåšçã«è©äŸ¡ãããåŒ(potentially-evaluated expression)ã§ã®ã¿ãåºçŸããŠããã
å®£èšæãforæ§æã®å®£èšã䌎ãåæåéšã§ã¯ããã®åæååã®äžã§ã®ã¿AwaitåŒãåºçŸããŠããã
ããã©ã«ãåŒæ°ã§ã¯AwaitåŒãçšããããšã¯ã§ããªãã
AwaitåŒã¯ãéçèšæ¶åãããã¯ã¹ã¬ããããŒã«ã«ãªãããã¯ã¹ã³ãŒã倿°ã®åæåã«åºçŸããŠã¯ãªããªãã
颿°å
ã§AwaitåŒã眮ããã³ã³ããã¹ããã颿°ã®äžæã³ã³ããã¹ããšåŒã¶ã
AwaitåŒã®è©äŸ¡ã§ã¯ã次ã®ãããªè£å©çãªåãåŒããªããžã§ã¯ããçšããïŒ
- p ãåAwaitåŒãå«ãã³ã«ãŒãã³ã®Promiseãªããžã§ã¯ãã®å·ŠèŸºå€åãšãã
Pãåãªããžã§ã¯ãã®åãšããã - a (Awaitable) ãäžèšã®ããã«å®çŸ©ããïŒ
- AwaitåŒãYieldåŒãŸãã¯åæãµã¹ãã³ããã€ã³ããŸãã¯æçµãµã¹ãã³ããã€ã³ãã«ããæé»ã«çæãããå Žåãa ããã® cast-expression ãšããã
Pã®ã¹ã³ãŒãã§é修食ãªawait_transformã®æ¢çŽ¢ã«ããäžã€ä»¥äžã®ååãã¿ã€ãã£ãå Žåã¯ã a ãp.await_transform( cast-expression )ãšããã- ãã以å€ã§ã¯ a ã cast-expression ãšããã
- o (Awaiter) ãäžèšã®ããã«å®çŸ©ããão ãprvalueã®å Žåã¯Temporary materialization conversionãè¡ãããïŒ
- å®åŒæ° a ã«å¯ŸããŠé©çšå¯èœãª
operator co_await颿°ãåæãão ããªãŒããŒããŒã解決ã«ããéžæããã颿°åŒã³åºããšããã - é©åãã颿°ãèŠã€ãããªãå Žåão ã a ãšããã
- ãªãŒããŒããŒãè§£æ±ºãææ§ãªå Žåãããã°ã©ã ã¯äžé©æ Œãšãªãã
- å®åŒæ° a ã«å¯ŸããŠé©çšå¯èœãª
- e ãão ã®è©äŸ¡çµæãåç §ãã巊蟺å€ãšããã
- h ããåAwaitåŒãå«ãã³ã«ãŒãã³ãåç
§ãã
std::coroutine_handle<P>åã®ãªããžã§ã¯ããšããã - await-ready ãã
boolã«å€æããããåŒe.await_ready()ãšããã - await-suspend ããåŒ
e.await_suspend( h )ãšããããã®åŒïŒã®çµæïŒã¯voidã§ããããboolãŸãã¯ä»»æã®åZã«å¯Ÿããstd::coroutine_handle<Z>åã®prvalueã§ããã¹ãã - await-resume ããåŒ
e.await_resume()ãšããã
AwaitåŒã¯åŒ await-resume ãšåãåãåãå€ã«ããŽãªãæã€ã
AwaitåŒã¯åŒ o ãšåŒ await-ready ãè©äŸ¡ããç¶ããŠïŒ
- await-ready ã®çµæã
falseã®å Žåãã³ã«ãŒãã³ã¯äžæç¶æ ãšã¿ãªãããããã®åŸã«ïŒ- await-suspend ã®åã
std::coroutine_handle<Z>ã®å Žåãawait-suspend.resume()ãè©äŸ¡ãããã - ããã§ã¯ãªã await-suspend ã®åã
boolã®å Žåãawait-suspend ãè©äŸ¡ããããã®çµæãfalseã§ããã°ã³ã«ãŒãã³ã¯åéããã - ãã以å€ã®å Žåãawait-suspend ãè©äŸ¡ãããã
- await-suspend ã®åã
- await-suspend ã®è©äŸ¡ãäŸå€ã§çµäºããå ŽåãäŸå€ãææãããŠã³ã«ãŒãã³ãåéãããã®äŸå€ã¯å³åº§ã«åéåºããããããã§ãªããã°ãã¹ã³ãŒãçµäºããšããªããã«çŸåšã®ã³ã«ãŒãã³ã®åŒåºå ãããã¯åéå ãžå¶åŸ¡ãããŒãæ»ãã
- await-ready ã®çµæã
trueãŸãã¯ã³ã«ãŒãã³ãåéããå Žåãawait-resume ã®è©äŸ¡çµæãAwaitåŒã®çµæãšãªãã
template <typename T>
struct my_future {
/* ... */
bool await_ready();
void await_suspend(std::coroutine_handle<>);
T await_resume();
};
template <class Rep, class Period>
auto operator co_await(std::chrono::duration<Rep, Period> d) {
struct awaiter {
std::chrono::system_clock::duration duration;
/* ... */
awaiter(std::chrono::system_clock::duration d) : duration(d) {}
bool await_ready() const { return duration.count() <= 0; }
void await_resume() {}
void await_suspend(std::coroutine_handle<> h) { /* ... */ }
};
return awaiter{d};
}
using namespace std::chrono;
my_future<int> h();
my_future<void> g() {
std::cout << "just about go to sleep...\n";
co_await 10ms;
std::cout << "resumed\n";
co_await h();
}
auto f(int x = co_await h()); // ãšã©ãŒ: awaitåŒã¯é¢æ°äžæã³ã³ããã¹ãã®å€
int a[] = { co_await h() }; // ãšã©ãŒ: awaitåŒã¯é¢æ°äžæã³ã³ããã¹ãã®å€
YieldåŒ
co_yieldåŒã¯ãã³ã«ãŒãã³ããå€ãçæ(yield)ãããšãã«çšããã
ãã®åäœã¯AwaitåŒã«ãŠæžãæãå¯èœã§ãããã³ã«ãŒãã³å©çšè
åãã®ã·ã³ã¿ãã¯ã¹ã·ã¥ã¬ãŒãšãè§£éã§ããã
co_yield assignment-expression
co_yield braced-init-list
YieldåŒã¯é¢æ°ã®äžæã³ã³ããã¹ãã«ã®ã¿åºçŸããŠããã
e ãYieldåŒã®ãªãã©ã³ããp ãååŒãå«ãã³ã«ãŒãã³ã®Promiseãªããžã§ã¯ãã®lvalueåãšãããšããYieldåŒã¯åŒco_await p.yield_value( e )ãšç䟡ã§ããã
template <typename T>
struct generator {
struct promise_type {
T current_value;
/* ... */
auto yield_value(T v) {
current_value = std::move(v);
return std::suspend_always{};
}
};
struct iterator { /* ... */ };
iterator begin();
iterator end();
};
generator<pair<int,int>> g1() {
for (int i = 0; i < 10; ++i) co_yield {i,i};
}
generator<pair<int,int>> g2() {
for (int i = 0; i < 10; ++i) co_yield make_pair(i,i);
}
auto f(int x = co_yield 5); // ãšã©ãŒ: yieldåŒã¯é¢æ°äžæã³ã³ããã¹ãã®å€
int a[] = { co_yield 1 }; // ãšã©ãŒ: yieldåŒã¯é¢æ°äžæã³ã³ããã¹ãã®å€
int main() {
auto r1 = g1();
auto r2 = g2();
assert(std::equal(r1.begin(), r1.end(), r2.begin(), r2.end()));
}
co_returnæ
co_returnæã¯ãã³ã«ãŒãã³ãçµäºãåŒåºå
ãžå¶åŸ¡ãæ»ãããã«çšããã
co_yieldïŒco_awaitããããå«ãŸãªãã³ã«ãŒãã³ãå®çŸ©ããå Žåã«ãå©çšã§ããã
co_return expr-or-braced-init-list opt ;
co_returnæãŸãã¯äžæã«ãããã³ã«ãŒãã³ã¯åŒåºå
ãããã¯åéå
ã«å¶åŸ¡ãæ»ãã
ã³ã«ãŒãã³ã¯éåžžã®returnæãå«ãã§ã¯ãªããªãã
co_returnæã® expr-or-braced-init-list ã¯ãªãã©ã³ããšåŒã°ããã
p ãã³ã«ãŒãã³Promiseãªããžã§ã¯ãã®lvalueåãšãããšãco_returnæã¯æ¬¡ãšç䟡ã§ããïŒ
{ S; goto final-suspend ; }
ããã§ final-suspend ã¯ã³ã«ãŒãã³åäœèª¬æçšã®æçµãµã¹ãã³ããã€ã³ãã©ãã«åã§ãããS ã¯æ¬¡ã®éãå®çŸ©ãããïŒ
- ãªãã©ã³ãã braced-init-list ãŸãã¯é
voidåã®åŒã®å ŽåãS ãp.return_value( expr-or-braced-init-list )ãšãããåŒ S ã¯voidåã®prvalueã§ããã¹ãã - ããã§ãªããã°ãS ãè€åæ
{ expressionopt ; p.return_void(); }ãšãããåŒp.return_void()ã¯voidåã®prvalueã§ããã¹ãã
p.return_void()ãæå¹ãªåŒã®ãšããã³ã«ãŒãã³æ¬äœã®çµç«¯å°éã¯ãªãã©ã³ãç¡ãco_returnãšç䟡ã§ããã
ããã§ãªããã°ãã³ã«ãŒãã³æ¬äœã®çµç«¯å°éã¯æªå®çŸ©ã®åäœãåŒãèµ·ããã
äŸ
#include <iostream>
#include <coroutine>
#include <utility>
#include <exception> // std::terminate()
// ã³ã«ãŒãã³å©çšã©ã€ãã©ãª: ãžã§ãã¬ãŒã¿å
struct my_generator {
// ãžã§ãã¬ãŒã¿ã«é¢é£ä»ããããPromiseå
struct promise_type {
// co_yieldåŒã§æå®ãããintå€ãä¿æãã倿°
int value_;
auto get_return_object()
{
// ã³ã«ãŒãã³ã«çŽã¥ãPromiseãªããžã§ã¯ã(*this)ãã
// ãžã§ãã¬ãŒã¿åã®ã³ã«ãŒãã³æ»ãå€ãªããžã§ã¯ããçæ
return my_generator{*this};
};
auto initial_suspend()
{
// ã³ã«ãŒãã³æ¬äœåŠçã®éå§åã«ç¡æ¡ä»¶ãµã¹ãã³ã
return std::suspend_always{};
}
auto final_suspend() noexcept
{
// ã³ã«ãŒãã³æ¬äœåŠçã®çµäºåŸã«ç¡æ¡ä»¶ãµã¹ãã³ã
return std::suspend_always{};
}
auto yield_value(int v)
{
// co_yieldåŒã§æž¡ãããå€ãä¿æããã³ã«ãŒãã³ãç¡æ¡ä»¶ãµã¹ãã³ã
value_ = v;
return std::suspend_always{};
}
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
// ãžã§ãã¬ãŒã¿ã«é¢é£ä»ããããã³ã«ãŒãã³ãã³ãã«å
using coro_handle = std::coroutine_handle<promise_type>;
// ç¯å²foræ§æãµããŒãçšã€ãã¬ãŒã¿å
struct iterator {
// 察象ã®ã³ã«ãŒãã³ãã³ãã«
coro_handle coro_;
// 察象ã³ã«ãŒãã³æ¬äœåŠçãçµäºãããã衚ããã©ã°
bool done_;
iterator& operator++()
{
// yield_value()ã§äžæããã³ã«ãŒãã³ãåéãã
coro_.resume();
// (co_yieldåŒè©äŸ¡ãããã¯ã³ã«ãŒãã³æ¬äœåŠçã®çµäºã«ããå¶åŸ¡ãæ»ã£ãŠãã)
done_ = coro_.done();
return *this;
}
bool operator!=(const iterator& rhs) const
{
return done_ != rhs.done_;
}
int operator*() const
{
// Promiseãªããžã§ã¯ããä¿æããŠããå€ãè¿ã
return coro_.promise().value_;
}
};
~my_generator()
{
if (coro_)
coro_.destroy();
}
my_generator(my_generator const&) = delete;
my_generator(my_generator&& rhs)
: coro_(std::exchange(rhs.coro_, nullptr)) {}
// ç¯å²foræ§æãµããŒãçšã®ã¡ã³ã颿°
iterator begin()
{
// initial_suspend()ã§äžæããã³ã«ãŒãã³ãåéãã
coro_.resume();
// (ååco_yieldåŒè©äŸ¡ã«ããå¶åŸ¡ãæ»ã£ãŠãã)
return {coro_, coro_.done()};
}
iterator end()
{
// çµç«¯äœçœ®ã衚çŸããçªå
µã€ãã¬ãŒã¿
return {{}, true};
}
private:
// Promiseãªããžã§ã¯ãçµç±ã§ã³ã«ãŒãã³ãã³ãã«ãååŸãã
explicit my_generator(promise_type& p)
: coro_(coro_handle::from_promise(p)) {}
coro_handle coro_;
};
// ãŠãŒã¶å®çŸ©ã³ã«ãŒãã³
my_generator iota(int end)
{
// ã³ã«ãŒãã³ã«å¯Ÿå¿ããPromiseå generator::promise_typeã®
// Promiseãªããžã§ã¯ã(p)ãçæãããã
for (int n = 0; n < end; ++n) {
// äžåŒã¯ co_await p.yield_value(n) ãšç䟡
co_yield n;
}
// ã³ã«ãŒãã³æ¬äœã®çµç«¯å°éã«ãã p.return_void() åŒã³åºã
}
int main()
{
// ã³ã«ãŒãã³ãåŒã³åºããæŽæ°çæãžã§ãã¬ãŒã¿ãååŸããã
auto g = iota(10);
// ãã®ã¿ã€ãã³ã°ã§ã¯ãŸã ã³ã«ãŒãã³æ¬äœã¯å®è¡ãããªãã
// ç¯å²foræ§æãçšããŠã³ã«ãŒãã³æ¬äœãå®è¡ããã
// ããã§ã¯ã³ã«ãŒãã³iotaã®å€çæã«ãŒãåŠçã¹ããããšã
// main颿°ã®è¡šç€ºã«ãŒãåŠçã¹ãããã亀äºã«å®è¡ãããã
for (int v: g) {
std::cout << v;
}
}
åºå
0123456789
ãã®æ©èœãå¿ èŠã«ãªã£ãèæ¯ã»çµç·¯
å€ãã®ããã°ã©ãã³ã°èšèªã§å¯Ÿå¿ãããŠããåºãå®çžŸã®ããã³ã«ãŒãã³æ©èœããC++èšèªã§ã䜿ãããã2013幎é ããæ€èšãå§ãŸã£ãŠããã
2017幎ã«ã¯ ISO/IEC TS 22277 C++ Extensions for CoroutinesïŒéç§°"Coroutines TS"ïŒ ãšããŠæ£åŒçºå¹ãããããã€ãã®è¿œå ã®ä»æ§ä¿®æ£ããžãŠC++20èšèªä»æ§æ¬äœãžã®çµ±åãæ±ºå®ãããã
C++èšèªä»æ§ãžã®ã³ã«ãŒãã³å°å ¥ã«ãã£ãŠããžã§ãã¬ãŒã¿ã®å調çãã«ãã¿ã¹ã¯ã®ãµããŒãããã¡ã€ã«ããããã¯ãŒã¯ãªã©éåæI/Oã©ã€ãã©ãªãšã®çµ±åãæåŸ ãããŠããã
æ€èšãããã»ãã®éžæè¢
C++20ã³ã«ãŒãã³ã¯ã¹ã¿ãã¯ã¬ã¹ã³ã«ãŒãã³ãšããŠå°å ¥ãããããã¹ã¿ãã¯ãã«(Stackful)ã³ã«ãŒãã³ïŒãã¡ã€ããŒ(Fiber)ã®å°å ¥æ€èšãé·ããè¡ãããŠããã ã¹ã¿ãã¯ãã«ã³ã«ãŒãã³ã¯å°æ¥ã®C++仿§å°å ¥ã«åããŠåŒãç¶ãæ€èšãããŠããã ïŒæ¬ããŒãžå·çæç¹ã§ã¯ææ¡ææžP0876R10ãææ°ïŒ
C++20ã³ã«ãŒãã³ã§ã¯ãã³ã«ãŒãã³ã»ã¹ããŒãã®ããã«åçã¡ã¢ãªç¢ºä¿ãè¡ãããå¯èœæ§ãããã äžå®æ¡ä»¶ãæºããã°ã³ã³ãã€ã©æé©åã«ãã£ãŠåçã¡ã¢ãªç¢ºä¿ãçç¥ããããšããŠããããèšèªä»æ§ãšããŠåçã¡ã¢ãªç¢ºä¿ãé¿ãã仿§ãæ€èšãããïŒéç§°"Core Coroutines"ïŒã æçµçã«ã¯æ¢ã«å®çžŸã®ããCoroutinesTSïŒçºæ¡è ã®ååã«ã¡ãªã¿"Gor-routines"ãšåŒã°ããïŒããŒã¹ã®ã³ã«ãŒãã³ä»æ§ãæ¡çšãããããšã«ãªã£ãã
C++20ã³ã«ãŒãã³ã«é¢ããããŒã¯ãŒãã¯ãããããæ¥é èŸco_ãä»äžãããŠããã
äœåºŠãã®æ¹åææ¡ïŒP0071R0ãP1485R1ïŒãæåºãããããããããåŠæ±ºãããŠC++20仿§ã«èœã¡çããã
é¢é£é ç®
- C++20
<coroutine> - C++23
<generator> - C++29 ã³ã«ãŒãã³ã®promiseåã§
return_valueãšreturn_voidã®äž¡æ¹ã®å®£èšãèš±å¯
åç §
- N4680 C++ Extensions for Coroutines(Coroutines TS)
- P0911R1 Rebase the Coroutines TS onto the C++17 Standard
- P0913R1 Add symmetric coroutine control transfer
- P0914R1 Add parameter preview to coroutine promise constructor
- P0664R4 C++ Coroutine TS Issues
- C++ Coroutines: Understanding operator co_await
- C++ Coroutines: Understanding the promise type
- C++ Coroutines: Understanding Symmetric Transfer
- C++ co_awaiting coroutines
- 20åãããã§ããã£ãæ°åã«ãªããC++20ã³ã«ãŒãã³