LoginSignup
2
3

More than 5 years have passed since last update.

D言語で引数のfunctionに@nogcが要求された時に、強引にnogcではないものを渡す方法

Posted at

どうも。
タイトル通りです。

この記事を書くきっかけとなったのが、core.stdc.signalsignalを使おうとした時に、引数の型がextern(C) @nogc nothrow void function(int)を要求していて、extern(C) void function(int)を渡そうとしてたけど@nogcに苦しめられていたのですが、先日Phobosのコミッターの方とご飯を食べに行った時に、「それ多分適当にキャストすればなんとかなるよ」って言われたのを思い出して試してみた感じです。

具体的にどういうことか。

次のコードは不正です。

test.d
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には@nogcnothrowもついていないからです。

どうすればいいか

で、どうすればいいか、→つけちゃえばいい。簡単ですね。
キャストして表面的につけてしまえばいいのです。
つまり、

test.d
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_executorhandler_func_2を渡すことが出来ます。
これはまぁ、強引にキャストすることで型チェックをごまかしているので通るのは当然ですね。
ただ、@nogcnothrowというのは、関数の特性を表すためのアノテーションであり、@nogcは内部でGCを使わないこと, nothrowはその関数が例外を吐かないことを約束することの表明ですが、non-@nogcthrowableなコードが@nogc nothrowの中で呼ばれるのは微妙な気分にはなりますね...(仕方ないですけど。)

で、結局どんなことができるのか

例えばプログラムがSIGINTを受け取った時に特定の処理を行いたい!となった時に、その処理が「non-@nogc かつ throwableな処理」であったときにその処理を行うことが出来ます。

test.d
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で終了された時に動いてるスレッドに対して停止命令を出したりできますね。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3