問題は、Tclコードの実行コンテキストであるインタープリターが、その足をそれ自体の下から削除していることです。これは非常に混乱します!少なくとも、再現が難しい不快なクラッシュではなく、クリーンなパニック/中絶が発生しています。
最も簡単な修正は、おそらく次のことです。
Tcl_Preserve(m_interpreter);
// Your code that calls Tcl_EvalFile(m_interpreter, script.c_str())
// and deals with the results.
Tcl_Release(m_interpreter);
Tcl_Release
の後に、Tcl_Interp
ハンドルが削除されたメモリを参照する場合があることに注意してください。(はい、 RAIIの良さでTcl_Preserve
/をラップするのTcl_Release
は合理的です。)
代わりに、スクリプトの実行後にコードの実行を許可する場合は、exit
追加の手順を実行する必要があります。特に、標準のTclexit
コマンドは、呼び出し元のコンテキストに戻るようには設計されていません。これにより、プロセスは_exit(2)
システムコールを呼び出します。動作を変更するには、次のように置き換えます。
// A callback function that implements the replacement
static int
MyReplacementExit(ClientData unused, Tcl_Interp *interp, int argc, const char *argv[])
{
// We ought to check the argument count... but why bother?
Tcl_DeleteInterp(interp);
return TCL_OK;
}
int main() {
Tcl_Interp *interp = Tcl_CreateInterp();
// Install that function over the standard [exit]
Tcl_CreateCommand(interp, "exit", MyReplacementExit, NULL, NULL);
// Important; need to keep the *handle* live until we're finished
Tcl_Preserve(interp);
// Or run whatever code you want here...
Tcl_Eval(interp, "exit");
// Important piece of cleanup code
if (!Tcl_InterpDeleted(interp))
Tcl_DeleteInterp(interp);
Tcl_Release(interp);
// After this point, you *MUST NOT* use interp
std::cout << "11111111111" << std::endl;
return 0;
}
この種のシナリオでメモリ管理を行うためのルールは、のマニュアルページに記載Tcl_CreateInterp
されています。(これは8.6のマニュアルページですが、少なくとも20年以上前のTcl 7.0以降、関連するルールは当てはまります。)インタプリタが削除されると、コマンドの実行やその中の変数へのアクセスを期待できなくなります。Tclライブラリは、状態の巻き戻しを処理します。