超高級C言語コンパイラの実力に迫る

# 概要

**D言語のコンパイラにはC言語をコンパイルする能力がある。つよい。**

# やりかた

```:ファイル構成
dub.json
src
  - main.d
  - foo.h
  - foo.c
```

```json:dub.json
{
	"name": "test",
	"cSourcePaths": ["src"],
	"targetType": "executable"
}
```

```c:foo.h
#include <stdint.h>
int32_t mul(int32_t a, int32_t b);
```

```c:foo.c
#include "foo.h"
#include <stdint.h>
#include <stdio.h>

int32_t mul(int32_t a, int32_t b)
{
	printf("a = %d, b = %d, a * b = %d\n", a, b, a * b);
    return a * b;
}
```

```d:main.d
import foo;
import std.stdio;

void main()
{
    writeln(mul(123, 45));
}
```

```:実行結果
$ dub build
    Starting Performing "debug" build using /home/shoo/dlang/dmd-2.106.0/linux/bin64/dmd for x86_64.
    Building test ~master: building configuration [application]
     Linking test
$ ./test
a = 123, b = 45, a * b = 5535
5535
```

もう少しビルドのところを詳細に見てみる。

```
$ dub build -f -v
Using dub registry url 'https://code.dlang.org/'
Note: Failed to determine version of package test at .. Assuming ~master.
Generating using build
Configuring dependent test, deps:
    Starting Performing "debug" build using /home/shoo/dlang/dmd-2.106.0/linux/bin64/dmd for x86_64.
    Building test ~master: building configuration [application]
[cwd=/mnt/r/test] /home/shoo/dlang/dmd-2.106.0/linux/bin64/dmd -c -of/home/shoo/.dub/cache/test/~master/build/application-debug-isal4KbJ4mWpL-aqDlpMfg/test.o -debug -g -w -version=Have_test -Isrc/ src/foo.c src/main.d -vcolumns
     Linking test
[cwd=/mnt/r/test] /home/shoo/dlang/dmd-2.106.0/linux/bin64/dmd -of/home/shoo/.dub/cache/test/~master/build/application-debug-isal4KbJ4mWpL-aqDlpMfg/test /home/shoo/.dub/cache/test/~master/build/application-debug-isal4KbJ4mWpL-aqDlpMfg/test.o -L--no-as-needed -g
Copying target from /home/shoo/.dub/cache/test/~master/build/application-debug-isal4KbJ4mWpL-aqDlpMfg/test to /mnt/r/test
```

全人類は以下の箇所に注目すべきである。
> /home/shoo/dlang/dmd-2.106.0/linux/bin64/**dmd** -c -of/home/shoo/.dub/cache/test/~master/build/application-debug-isal4KbJ4mWpL-aqDlpMfg/test.o -debug -g -w -version=Have_test -Isrc/ **src/foo.c** src/main.d -vcolumns

**D言語コンパイラでC言語のソースコードをコンパイルしている**


# 解説
これを可能としているのは、D言語の仕様である、 `ImportC` という[仕様](https://dlang.org/spec/importc.html#module-name)だ。
ある日D言語の創始者であるWalter氏は思いついてしまったのだ。D言語コンパイラでC言語もコンパイルできるようにしちゃったらいいんじゃないの、と。試しにやってみたらちょい変でできちゃった…と。
<font color=white>
C++はC言語をコンパイルできるようにしたことは互換性で制約ができるから失敗だったとか言ってた気がするけど、私の記憶違いだったようだ
</font>

ファイル構成については、一般的なdubを使用する場合のファイル構成と同じで大丈夫である。
同じソースコードのフォルダ内に.cや.h、.dを混在させてもよいし、C言語のソースコードだけ別フォルダに分けてもよい。いずれにしても `cSourcePaths` のdubプロジェクト設定でC言語ソースコードを置いたフォルダを指定することで、C言語のソースコードをコンパイルするようになる。

C言語のソースコードをコンパイルできることで、C言語とD言語の相互運用性が高まる。素晴らしい。
例に挙げたようにD言語からC言語のコードを呼び出すこともできるし、C言語のソースコードをコンパイル時に実行することも可能である。C言語からD言語コードを呼ぶこともできる。
この機能を使うことで、とりあえずzlib程度のライブラリはコンパイルすることが可能なようだ。
<sub>……ということはあれ？Excelファイルをコンパイル時に開いてセルから情報読み取ってコード生成したりとかもできるのでは……？</sub>

# 現在の制約事項
## プリプロセッサは搭載されていない
プリプロセッサは別のプログラムを持ち込む必要がある。[仕様的にはこの辺](https://dlang.org/spec/importc.html#preprocessor)。
これは別にD言語コンパイラだからというわけでもない。gccだってC言語コンパイラとは別のプリプロセッサプログラムを呼び出して前処理していたりする。
ただし、gccがプリプロセッサを自前で用意しているのとは違い、D言語のコンパイラは自前でプリプロセッサを提供していない。別のコンパイラセットのプリプロセッサに依存する形となる。例えばWindowsであればGCCやVisualStudioのプリプロセッサに便乗する形となる。
特にWindowsでVisualStudioのプリプロセッサに依存する場合は、現時点では`INCLUDE`などの環境変数が[正しく通っている必要がある](https://issues.dlang.org/show_bug.cgi?id=24111)。vcvarall.batなどでパスや環境変数を準備しておくのが良い。

また、これに関連して、現時点でインクルードパスの指定がdubであっても面倒なところがある。
使用するプリプロセッサにより、インクルードパスの指定方法が異なるため、`dflags`オプションで`"dflags": ["-P-Iinclude"]`のように指定する必要がある。この問題は[不具合報告済みの案件](https://github.com/dlang/dub/issues/2698)なので、じきに解決するかもしれない。

## 制限事項など
いくつかのC言語機能は完全に模倣できていないものがある。[仕様的にはこの辺](https://dlang.org/spec/importc.html#limitations)。
また、コンパイラの独自実装のようなものへの対応は限定的だ。[仕様的にはこの辺](https://dlang.org/spec/importc.html#gnu-clang-extensions)。
これらの制限事項などは、たいていの場合には問題にならない。
しかし、コンパイラが独自な場合く、制限事項に挙げられている機能の多くを使いこなす必要がある組み込みソフトなんかではこの制限事項は厳しいかもしれない。
ある程度は、`importc.h`というファイルを配置して、マクロ定義で無理やり書き換えるなどごり押しすることもできるかもしれない。

## その他
C言語のファイルには、現時点ではパッケージ名が設定されない。
そもそもD言語はディレクトリ階層でパッケージ名が決まるが、C言語はディレクトリ階層を無視する仕様であるため、仕様がかみ合わない。
このため、C言語のファイル名はほかのパッケージ名と被らないよう注意が必要であるし、ディレクトリ階層を使用しない平置きにしてやったほうが良さそうだ。