web-dev-qa-db-ja.com

C ++でコルーチンをどのように実装しますか

移植性があるとは思いませんが、解決策はありますか?代替スタックを作成し、関数エントリでSP、BP、およびIPをリセットし、yieldでIPを保存し、SP + BPを復元することで実現できると思います。デストラクタと例外の安全性は扱いにくいようですが、解決可能です。

終わった?不可能ですか?

62
Mike Elkins

はい、問題なくできます。必要なのは、呼び出しスタックをヒープに新しく割り当てられたスタックに移動するための小さなアセンブリコードです。

私はboost :: coroutine libraryを見るでしょう。

気を付けなければならないことの1つは、スタックオーバーフローです。ほとんどのオペレーティングシステムでは、仮想メモリページがマッピングされていないため、スタックがオーバーフローするとセグメンテーション違反が発生します。ただし、ヒープにスタックを割り当てた場合、保証はありません。覚えておいてください。

88
Ted

POSIXでは、makecontext()/ swapcontext()ルーチンを使用して、実行コンテキストを移植可能に切り替えることができます。 Windowsでは、ファイバーAPIを使用できます。それ以外の場合、必要なのは、マシンコンテキストを切り替える接着アセンブリコードのビットです。 ASM(AMD64用)とswapcontext()の両方でコルーチンを実装しました。どちらも非常に難しいことではありません。

18
zvrba

後世のために、

Dmitry Vyukovの wondeful web site には、ucontextとsetjumpを使用したc ++のシミュレートされたコルーチンへの巧妙なトリックがあります。

また、Oliver Kowalkeのコンテキストライブラリが 最近受け入れられた Boostに追加されたため、x86_64ですぐに機能するboost.coroutineの更新バージョンが表示されることを期待しています。

15
tgoodhart

コルーチンを実装する簡単な方法はありません。コルーチン自体は、スレッドのようにC/C++のスタック抽象化から外れているためです。したがって、サポートする言語レベルを変更しないとサポートできません。

現在(C++ 11)、既存のすべてのC++コルーチン実装はすべて、アセンブリレベルのハッキングに基づいています。信頼性を高めるには、標準であり、ハッキングではなくコンパイラーによって処理される必要があります。

標準提案-N3708 があります。興味のある方はチェックしてください。

10
Eonil

可能であれば、コルーチンよりもイテレータを使用した方が良いかもしれません。そうすれば、next()を呼び出して次の値を取得できますが、ローカル変数ではなくメンバー変数として状態を保持できます。

物事をより保守しやすくするかもしれません。別のC++開発者は、コルーチンをすぐには理解しないかもしれませんが、イテレーターに慣れているかもしれません。

7
Steve g

C++でポータブルな方法でコルーチンをどのように活用できるかを知りたい場合は、y ++標準化委員会は、この機能に取り組んでいます N3722論文 を参照してください。非同期および待機の代わりに、論文の現在のドラフトを要約すると、キーワードは再開可能で待機します。

Visual Studio 2015の実験的な実装を見て、Microsoftの実験的な実装を試してください。 clangにはまだ実装されていないようです。

Cppconから良い話があります コルーチンは負のオーバーヘッド抽象化 C++でコルーチンを使用する利点と、それがコードのシンプルさとパフォーマンスにどのように影響するかを概説します。

現時点では、まだライブラリの実装を使用する必要がありますが、近い将来、コアC++機能としてコルーチンを使用する予定です。

更新:コルーチンの実装はC++ 20に予定されているようですが、C++ 17で技術仕様としてリリースされました( p0057r2 )。 Visual C++、clang、およびgccでは、コンパイル時フラグを使用してオプトインできます。

6
Atifm

COROUTINEコルーチンシーケンス用のポータブルC++ライブラリ は正しい方向を示していますか?それは時の試練に耐えたエレガントなソリューションのように思えます.....それは9歳です!

DOCフォルダーは、Keld Helsgaunによるコルーチンシーケンス用のポータブルC++ライブラリのライブラリです。

[更新]実際に自分で使用しています。好奇心が私を良くしてくれたので、私はこの解決策を検討し、それが私がしばらく取り組んできた問題に適していることを発見しました!

5
Dan

C++には本格的でクリーンな実装がたくさんあるとは思わない。私が気に入っているのは、 Adam Dunkelsのprotothread library です。

Protothreads:メモリ制約のある組み込みシステムのイベント駆動型プログラミングの簡素化 ACM Digital Libraryのウィキペディアのトピックでの議論 Protothread

5
yrp

新しいライブラリ(Boost.Context)は、コルーチンを実装するためのポータブル機能を備えて本日リリースされました。

3
Jeff Trull

これは古いスレッドですが、OSに依存しないDuffのデバイスを使用したハックをお勧めします(覚えている限り)。

Duffのデバイスを使用したCコルーチン

また、例として、fork/threadsの代わりにコルーチンを使用するように変更したtelnetライブラリを次に示します。 coroutines を使用するTelnet cliライブラリ

また、C99より前の標準Cは本質的にC++の真のサブセットであるため、C++でもうまく機能します。

3
Erik Alapää

私は実装を考え出しましたasmなしコード。アイデアは、システムのスレッド作成関数を使用してスタックとコンテキストを初期化し、setjmp/longjmpを使用してコンテキストを切り替えることです。しかし、移植性はありません。興味のある方は tricky pthread version をご覧ください。

2
rox

(cringe)マクロに基づいていますが、次のサイトは使いやすいジェネレーター実装を提供します。 http://www.codeproject.com/KB/cpp/cpp_generators.aspx

2
Mark

私の実装を確認してください、それはasmハッキングポイントを示しており、簡単です:

https://github.com/user1095108/generic/blob/master/coroutine.hpp

1
user1095108

https://github.com/tonbit/coroutine は、resume/yield/awaitプリミティブとチャネルモデルをサポートするC++ 11シングル.h非対称コルーチン実装です。ブーストに依存せず、linux/windows/macOS上で実行されるucontext/fiberを介して実装されます。 C++でコルーチンを実装することを学ぶのは良い出発点です。

1
atto_mu

代わりに、常にスレッドの使用を検討する必要があります。特に最新のハードウェアで。コルーチンで論理的に分離できる作業がある場合、スレッドを使用すると、実際には作業が別々の実行ユニット(プロセッサコア)によって同時に実行される可能性があります。

しかし、コルーチンを使用したいのは、おそらくその方法で既に作成およびテストされた十分にテストされたアルゴリズムがあるか、その方法で作成されたコードを移植しているからです。

Windows内で作業する場合は、 fibers をご覧ください。 Fibersは、OSのサポートによりコルーチンのようなフレームワークを提供します。

私は他のOSに精通していないので、他のOSを推奨します。

0
Euro Micelli

WvCont は、いわゆるセミコルーチンを実装する WvStreams の一部です。これらは、完全なコルーチンよりも少し簡単に処理できます。あなたはそれを呼び出し、それを呼び出した人に譲ります。

完全なコルーチンをサポートする、より柔軟なWvTaskを使用して実装されています。同じライブラリで見つけることができます。

少なくともwin32とLinuxで動作し、おそらく他のUnixシステムでも動作します。

0
apenwarr