static thisについてちゃんと理解していますか?


## static this と shared static thisの説明

D言語には`static constructor`(日本語で言うと静的コンストラクタ)という言語機能が存在します。(以下の説明では`static this`と`static constructor`を同義とします。)
名前から想像がつくようにstaticなコンストラクタで、すなわち、main関数が実行されるよりも前に実行されるコンストラクタで、主にモジュールやクラスを初期化するために使います。
(なお、説明ではコンストラクタのみを説明していますが、デストラクタについても同様です。詳しくは言語仕様を参照してください。)

シングルトンパターンでクラスを書く場合などは`static this`を使うと便利だと思います。(僕は殆どシングルトンパターンでプログラムを書く(/書いた)ことはないので、次に示す例が理想的なシングルトンパターンであるかどうかはわかりません。)
適当な例としては次のようなコードです。

```d:singleton.d
final class Singleton {
  // 唯一のインスタンス。
  private static Singleton singleton;
  private this() {}

  // main関数よりも先に呼び出され、singletonにインスタンスを代入する。(Singletonクラスを静的に初期化する)
  static this() {
    singleton = new Singleton;
  }

  @property static typeof(this) getInstance() {
    return singleton;
  }
}
```

```d:main.d
/**
 * Compile: $ dmd main.d singleton.d
 */

import singleton;

void main() {
  // this()をprivateにしてあるので、次のようにしてインスタンスが作られることはない。
  // Singleton singleton = new Singleton();
  Singleton singleton = Singleton.getInstance;
}
```

このように使う限りでは、`static this`についてこれ以上の理解は必要が無いように思われます。


さて、先ほど`static this`はmain関数よりも先に実行されると書きました。
それを確かめてみましょう。

```d
import std.stdio;

int[] ar;

static this() {
  ar ~= 0;
}

void main() {
  ar ~= 1;

  assert (ar == [0, 1]);
}
```

assertionは成功します。
では、複数の`static this`が`同一module内に複数`あった場合どのようになるのでしょうか。
これは、D言語の言語仕様のModulesのページの[Static Construction and Destruction](https://dlang.org/spec/module.html#staticorder)にも書いてあるとおり、ソースコードに書かれた順に実行されます。
> There can be multiple static constructors and static destructors within one module. The static constructors are run in lexical order, the static destructors are run in reverse lexical order.

(run in lexical orderが相当します)

実験してみても良いでしょう:

```d
import std.stdio;

static this() {
  writeln("First");
}

static this() {
  writeln("Second");
}

void main() {
  writeln("Main");
}

/**
 * Output:
 *   First
 *   Sencond
 *   Main
 */
```

また、`static this`の仲間に`shared static this`というものも有ります。
両者の違いは、`static this`がスレッドローカルなメモリ領域で動くのに対し、`shared static this`はグローバルなメモリ領域で動くという違いがあります。
簡単にいえば、`static this`はスレッドを作るたびに呼ばれますが、`shared static this`はプログラムが起動された時に一度実行されるだけです。
これも簡単に実験ができます。

```d
import std.stdio;
import core.thread;

static this() {
  writeln("static this was called");
}

shared static this() {
  writeln("shared static this was called");
}

void main() {
  foreach(_; 0..2) {
    new Thread({}).start;
    new Thread({}).start;
  }
}

/**
 * Output:
 *   shared static this was called
 *   static this was called
 *   static this was called
 *   static this was called
 */
```

ちゃんと3回、`static this was called`が出力されていることから、スレッド作成時に`static this`が呼ばれていることが確認できますね。
また、出力には違和感があるかもしれませんね。というのも、`static this`のほうが`shared static this`よりも先に書かれているのに、実行順序が逆になっています。
これは、`shared static this`のほうが`static this`よりも先に呼ばれるようになっているからです。もちろん、`shared static this`を複数書いた場合は、書かれた順番に(ソースコード的に上から)実行されます。(その後に`static this`が呼ばれる。)
なお、デストラクタの場合はコンストラクタと実行される順番が異なり、書かれた順番とは逆の順番で実行されます。
`a, b`をコンストラクタ, `c, d`をデストラクタとして`a -> b -> c -> d`という順番でコードを書いた場合、呼ばれる順番は
`a -> b -> d -> c`
となります。
また、静的デストラクタのうち、`shared static ~this`はコンストラクタとデストラクタの実行順序の関係と同様に、コンストラクタとは逆の順番で実行されます。つまり、一番最後に`shared static ~this`は実行されます。


さて、`static this`についての説明は次で終わります。

今までは、同一モジュール内(単一ファイル内)に複数の`static this`を置いた場合を考えてきました。次は、複数のモジュール(複数ファイル)を用いた場合について見てみましょう。

初めにソースコードから:

```d:ma.d
import std.stdio;
static this() {
  writeln("static this[ma] was called");
}
```

```d:mm.d
/**
 * Compile: $ dmd mm.d ma.d
 */

import std.stdio;

static this() {
  writeln("static this[mm] was called");
}

import ma;

void main() {}
```

このように考えた時、単純に考えると`static this`の次に`import ma;`でmaモジュール(D言語では明示的にmodule名を書かないかぎり、ファイル名がそのままモジュール名になってくれます)をimportしているので

```
static this[mm] was called
static this[ma] was called
```

と出力される、と考えてしまうでしょう。

しかし、実際には

```
static this[ma] was called
static this[mm] was called
```

と出力されます。
これは、先ほどの言語仕様の[Order of Static Construction](https://dlang.org/spec/module.html#order_of_static_ctor)にも書いてある正しい挙動です。

> The order of static initialization is implicitly determined by the import declarations in each module. Each module is assumed to depend on any imported modules being statically constructed first. Other than following that rule, there is no imposed order on executing the module static constructors.


`Each module is assumed to depend on any imported modules being statically constructed first.`が該当します。
すなわち、importしている側のコードはimportしているすべてのモジュールに依存していると推測され、最初にすべて解決(static constructorが呼ばれる)されます。

それゆえ、セマンティクス的にはmm -> maという順番で出力されるように思えますが、実際にはma -> mmという順番で出力されます。

とりあえず、ここまでが`static this`と`shared static this`についての説明です。

ちなみに、さらっと最初のコードで、classの中に`static this`を書きましたが、そのようにclassの中とstructの中にも`static this`を書くことが出来ます。

## さて、どうするとまずいか
これも言語仕様に書いてあるのですが(引用及びリンクは後で貼ります)、具体的には以下のコードは実行時にランタイム例外が発生します。

```d:a.d
import b;

static this() {}
```

```d:b.d
import a;

static this() {}
```

```d:main.d
/**
 * Compile: $ dmd main.d a.d b.d
 */
import a;

void main() {}
```

これを実行すると、前述のとおりランタイム例外が発生します。
出力されるエラーは以下:

```
object.Exception@src/rt/minfo.d(167): Aborting: Cycle detected between modules with ctors/dtors:
a* ->
b* ->
a
```

で、これが何を意味しているのかというと、「循環が発生しているよ!!」 ということです(そのままですが)。

言語仕様でもこの挙動は定義されています([Order of Static Construction](https://dlang.org/spec/module.html#order_of_static_ctor)):

> Cycles (circular dependencies) in the import declarations are allowed as long as not both of the modules contain static constructors or static destructors. Violation of this rule will result in a runtime exception.

ようするに、循環するようなimport(つまり、aがbをimport, bがaをimportしている状態。上に書いたコードと構成は同じ状況)は、それらがともに静的コンストラクタを持たないかぎりは許容されるが(aのみが静的コンストラクタを持つという場合は問題ない)、そうでない場合、すなわち、(上のサンプルコードのように)両方のモジュールが互いにそれぞれ静的コンストラクタを保つ場合は実行時に例外が発生するということです。


上の問題の解決策はいくつかあるとは思いますが、一つの解決策としては次のようなものがあると思います:

```d:a.d
import b;

//static thisによる初期化をやめ、staticな初期化用の関数を定義
private static bool initialized;

static initialize_A() {
  if (!initialized) {
    import std.stdio;

    writeln("initialize_A");
    initialized = true;
  }
}

// 初期化してあるかどうか
static bool isInizializedA() {
  return initialized;
}
```

```d:b.d
import a;

// aと同様
private static bool initialized;

static initialize_B() {
  if (!initialized) {
    import std.stdio;
    
    writeln("initialize_B");
    initialized = true;
  }
}

// 初期化してあるかどうか
static bool isInizialized_B() {
  return initialized;
}
```

```d:c.d
//aとbを初期化するためのモジュール
import a;
import b;

//ここで初期化をする
static this() {
  initialize_A;
  initialize_B;
}

//初期化できてるかどうかの確認
static checkInitialized() {
  import std.stdio;

  writeln("a -> ", isInizialized_A);
  writeln("b -> ", isInizialized_B);
}
```

```d:main.d
import c;

void main() {
  //cを経由してa, bが初期化できているかを確認する。
  checkInitialized;
  
  //こちら側にも反映されているかどうかをテスト
  import a;
  import b;
  import std.stdio;
  writeln("a -> ", isInizialized_A);
  writeln("b -> ", isInizialized_B);
}
```

このように書けば、importの循環を回避することが出来ます。


## なぜこの記事を書いたか
僕が言語仕様を読んでなくて勝手にimportの循環に陥って軽くハマりかけたので、書くことにしました。

結論: 言語仕様を読みましょう。