GCCでこのようなことをすることは可能ですか?
void foo() {
if (something()) returnSomewhereElse;
else return;
}
void bar() {
foo();
return; // something failed, no point of continuing
somewhereElse:
// execution resumes here if something succeeds
// ...
}
- これは、プラットフォーム固有のアセンブリを使用せずに、C および GCC 拡張機能を使用して移植可能な方法で実現できますか?
- スタック状態は、通常の戻りポイントと変更された戻りポイントの間で変化しないため、通常の戻りからスタックとレジスタの状態を復元するコードを再利用することは可能ですか?
- 関数がインライン化される場合とされない場合があることを考慮して、呼び出された場合はリターン アドレスを変更する必要があります。インライン化されている場合は、コード パスのみを変更する必要がありますが、現在の関数のリターン アドレスは変更しないでください。コードが壊れる可能性があるためです。
- 代替リターンポイントはラベルである必要はありませんが、GCC のラベル拡張のアドレスがこの状況で役立つことを願っています。
意図を明確にするために-それはエラー処理に関するものです。この例は、物事を説明するための最小限のものです。エラーが発生した場合に実行を停止するために、これをより深いコンテキストで使用するつもりです。また、2 つの戻り点の間に余分なローカル変数が追加されていないため、状態が変化しないと仮定しますが、これについては間違っている可能性があります。そのためにlongjmp
、ジャンプバッファーを使用、設定、および渡すオーバーヘッドを節約します。
その意図は、実際のコードでなぜ、どのように意味があるかではなく、私が達成したいことを示すことであるため、この例は「意味があります」。
単純に foo() から値を返し、bar() を何らかの条件で返すか実行するよりも、あなたのアイデアが単純なのはなぜですか?
それは単純ではなく、あなたが提案することは実際には適用できず、些細な例のコンテキストでのみ適用されますが、次の理由により優れています。
1 - 値を追加で返す必要はありません
2 - 値の追加チェックは必要ありません
3 - 追加のジャンプは必要ありません
私はおそらく、この時点で、すべての明確化と説明を経て、目標が明確になるはずだと誤って想定しています。アイデアは、追加のオーバーヘッドなしで深い呼び出しチェーンから「エスケープ コード パス」を提供することです。コンパイラが生成したコードを再利用して、前の呼び出しフレームの状態を復元し、関数が戻った後に実行を再開する命令を変更するだけです。成功すると「エスケープ コード パス」がスキップされ、発生した最初のエラーがそこに入ります。
if (failure) return; // right into the escape code path
else {
doMagickHere(); // to skip the escape code path
return; // skip over the escape code path
}
//...
void bar() {
some locals;
foo();
// enter escape code path here on foo failure so
destroy(&locals); // cleanup
return; // and we are done
skipEscapeCodePath: // resume on foo success
// escape path was skipped so locals are still valid
}
Basile Starynkevitch によるlongjmp
「効率的」であり、「10 億の longjmp でも妥当なままである」という主張についてはsizeof(jmp_buf)
、156 バイトというかなりの量が得られます。後で復元できるようにします。これらは多くの操作であり、それを10億回行うことは、「効率的」および「合理的」という私の個人的な理解からはほど遠いものです。つまり、10 億個のジャンプ バッファー自体が145 ギガバイトを超えるメモリだけであり、CPU 時間のオーバーヘッドもあります。そのような「合理的な」余裕さえあるシステムはそれほど多くありません。