どうも。
タイトル通りです。
この記事を書くきっかけとなったのが、core.stdc.signal
のsignal
を使おうとした時に、引数の型がextern(C) @nogc nothrow void function(int)
を要求していて、extern(C) void function(int)
を渡そうとしてたけど@nogc
に苦しめられていたのですが、先日Phobosのコミッターの方とご飯を食べに行った時に、「それ多分適当にキャストすればなんとかなるよ」って言われたのを思い出して試してみた感じです。
具体的にどういうことか。
次のコードは不正です。
import std.stdio;
import core.stdc.stdio;
alias handler_t = @nogc nothrow void function();
void handler_executor(handler_t handler) {
handler();
}
@nogc nothrow void handler_func_1() {
printf("handler_func_1!\n");
}
void handler_func_2() {
writeln("handler_func_2!");
}
void main() {
handler_executor(&handler_func_1);
handler_executor(&handler_func_2);//←ここがダメ
}
//←ここがダメ
と書いた部分がダメです。というのも、handler_func_2
には@nogc
もnothrow
もついていないからです。
どうすればいいか
で、どうすればいいか、→つけちゃえばいい。簡単ですね。
キャストして表面的につけてしまえばいいのです。
つまり、
import std.stdio;
import core.stdc.stdio;
alias handler_t = @nogc nothrow void function();
void handler_executor(handler_t handler) {
handler();
}
@nogc nothrow void handler_func_1() {
printf("handler_func_1!\n");
}
void handler_func_2() {
writeln("handler_func_2!");
}
void main() {
handler_executor(&handler_func_1);
handler_executor(cast(handler_t)&handler_func_2);//←これならok
}
これで、@nogc
がついてないwriteln
を内部で呼んでいるのにも関わらずhandler_executor
にhandler_func_2
を渡すことが出来ます。
これはまぁ、強引にキャストすることで型チェックをごまかしているので通るのは当然ですね。
ただ、@nogc
やnothrow
というのは、関数の特性を表すためのアノテーションであり、@nogc
は内部でGCを使わないこと, nothrow
はその関数が例外を吐かないことを約束することの表明ですが、non-@nogc
やthrowable
なコードが@nogc nothrow
の中で呼ばれるのは微妙な気分にはなりますね...(仕方ないですけど。)
で、結局どんなことができるのか
例えばプログラムがSIGINT
を受け取った時に特定の処理を行いたい!となった時に、その処理が「non-@nogc かつ throwable
な処理」であったときにその処理を行うことが出来ます。
import std.stdio;
import core.stdc.stdlib,
core.stdc.signal;
alias sighandler_t = extern(C) @nogc nothrow void function(int);
void sigint_handler(int s) {
writeln("SIGINT!!!");
exit(0);
}
void main() {
signal(SIGINT, cast(sighandler_t)&sigint_handler);
while (true) {
writeln("LOOP!");
}
}
こんな感じのコードが書けます。
これを応用すると、プログラムがSIGINT
で終了された時に動いてるスレッドに対して停止命令を出したりできますね。