古いCAPIを使用するC++でライブラリを作成しています。私のライブラリのクライアントは、CAPIを介して呼び出される私のライブラリを介して間接的に呼び出されるコールバック関数を指定できます。これは、クライアントコールバックのすべての例外を処理する必要があることを意味します。
私の質問はこれです:C API境界が再交差し、実行がC ++ランドに戻ったら、境界の片側で例外をキャッチして再スローし、クライアントコードで例外を処理できるようにするにはどうすればよいですか?
古いCAPIを使用するC++でライブラリを作成しています。私のライブラリのクライアントは、CAPIを介して呼び出される私のライブラリを介して間接的に呼び出されるコールバック関数を指定できます。これは、クライアントコールバックのすべての例外を処理する必要があることを意味します。
私の質問はこれです:C API境界が再交差し、実行がC ++ランドに戻ったら、境界の片側で例外をキャッチして再スローし、クライアントコードで例外を処理できるようにするにはどうすればよいですか?
C ++ 11では、次のものを使用できます。
std::exception_ptr active_exception;
try
{
// call code which may throw exceptions
}
catch (...)
{
// an exception is thrown. save it for future re-throwing.
active_exception = std::current_exception();
}
// call C code
...
// back to C++, re-throw the exception if needed.
if (active_exception)
std::rethrow_exception(active_exception);
C ++ 11より前でも、これらはBoostExceptionを介して使用できます。
一部の環境では、これを多かれ少なかれ直接サポートしています。
たとえば、コンパイラスイッチを介して構造化例外処理とC ++例外を有効にすると/EH
、Microsoftの構造化例外処理(Cの「例外」)にC++例外を実装できます。これらのオプションがすべてのコード(両端のC ++と中央のC)をコンパイルするときに設定されている場合、スタックの巻き戻しは「機能」します。
ただし、これはほとんどの場合、悪いアイデア(TM)です。 なぜ聞くの?真ん中のCコードは次のとおりです。
WaitForSingleObject(mutex, ...);
invoke_cxx_callback(...);
ReleaseMutex(mutex);
そして、invoke_cxx_callback()
(....ドラムロール...)が例外をスローするC++コードを呼び出すこと。ミューテックスロックをリークします。痛い。
ご覧のとおり、ほとんどのCコードは、関数の実行中のどの時点でもC++スタイルのスタックの巻き戻しを処理するように記述されていません。さらに、デストラクタがないため、例外から自身を保護するためのRAIIがありません。
Kenny TMには、C++11およびBoostベースのプロジェクト向けのソリューションがあります。 xxbbccには、一般的なケースではより退屈な解決策がありますが、より一般的なものがあります。
おそらく、例外の場合にエラー情報で埋められる構造をCインターフェースに渡して、それがクライアント側で受信されたら、それをチェックして、構造からのデータに基づいてクライアント内で例外をスローすることができます。例外を再現するために最小限の情報しか必要としない場合は、おそらく32ビット/64ビット整数をエラーコードとして使用できます。例えば:
typedef int ErrorCode;
...
void CMyCaller::CallsClient ()
{
CheckResult ( CFunction ( ... ) );
}
void CheckResult ( ErrorCode nResult )
{
// If you have more information (for example in a structure) then you can
// use that to decide what kind of exception to throw.)
if ( nResult < 0 )
throw ( nResult );
}
...
// Client component's C interface
ErrorCode CFunction ( ... )
{
ErrorCode nResult = 0;
try
{
...
}
catch ( CSomeException oX )
{
nResult = -100;
}
catch ( ... )
{
nResult = -1;
}
return ( nResult );
}
単一のint32/int64よりも多くの情報が必要な場合は、呼び出しの前に構造体を割り当て、そのアドレスをC関数に渡すことができます。これにより、内部で例外がキャッチされ、発生した場合は、それ自体で例外がスローされます。