ClangにModulesという機能を知り、公式リファレンスを要約いたしました。
公式リファレンス
http://clang.llvm.org/docs/Modules.html
0.Clang Modulesの機能について
-
ヘッダーをコンパイルし、バイナリー表現のモジュールに変換する。
-
1度モジュールに変換したヘッダーは、変更しない限り再度コンパイルされない。
-
モジュールに変換したヘッダーは、includeされると自動的にモジュールがimportされる。
-
モジュール同士は独立しているため、マクロがincludeされることによるソースコードの破壊を防ぐことが出来る。
-
Objective-Cの場合は、明示的にモジュールを以下のようにインポートできる。
@import Foundation; @import AVFoundation; //AVFoundation.framworkのリンクを追加する必要がない。
Note
Xcode 6からは自動的にModulesが有効となっているため、
#import <Foundation/Foundation.h> は自動的に裏で@import Foundation;と書き換えられている。
ゆえにフレームワークのリンクの追加が必要となくなった。
#1. ヘッダーをモジュール化する
1-1 Clang Modulesを使用する方法
- module.modulemapファイルを作成し、ヘッダーファイルと一緒に配置する。
- -fmodulesオプションをつけてコンパイルする。
$ clang -fmodules main.c -o main.o
上記のようにソースコード側でモジュールを使用するための記述は一切いらないため
他のコンパイラで使用されるコード
1-2 module.modulemapファイルとモジュールマップ言語
ヘッダーをモジュール化するには、
module.modulemapファイルにモジュール宣言を行う。
/*モジュール宣言
a.hファイルをモジュールAに変換
*/
module A {
header "a.h"
}
//モジュール宣言
module B {
header "b.h"
}
//モジュール宣言
module Set {
header "s1.h"
header "s2.h"
}
上記のようにmodule.modulemapファイルに、
モジュール宣言を行うことでヘッダーをモジュール化する。
また複数のヘッダーファイルを1つのモジュールへと変換することも可能。
Clangは、モジュールに変換されたヘッダーファイルがincludeされると自動的に該当するモジュールをimportする。
1-3 モジュールマップ言語
1-3-1 モジュール宣言
基本構文
[explictオプション] [frameworkオプション] module module-id [属性] {
/*モジュールメンバ*/
}
module-idはユニーク名である必要がある。
モジュールメンバには以下の宣言が行える。
- header
- umbrella directory
- umbrella header
- submodule
- export
- Use
モジュール宣言の例
module Hello {
//コメントも可能
header "hello.h"
export *
}
hello.hファイルをモジュールHelloへと変換する。
1-3-2 header宣言
header declaration
機能
該当モジュールにヘッダーファイルを変換する。
基本構文
header "ヘッダーファイル"
hedaer宣言の例
module Module {
header "a.h"
header "b.h"
}
モジュール機能が有効であれば、
a.hファイル、b.hファイルがincludeされると、
Clangは自動的にModuleモジュールをimportする。
1-3-3 umbrella directory宣言
umbrella directory declaration
機能
ディレクトリ内にあるヘッダーファイル一覧を全てそのままモジュールに追加する。
基本構文
umbrella "ディレクトリ名"
umbrella directory宣言の例
module MyLib {
umbrella "MyLib"
}
MyLibディレクトリ配下のヘッダーファイルが全てMyLibモジュールに追加される。
1-3-4 umbrella header宣言
umbrella header declaration
機能
ヘッダーファイルにincludeされているヘッダーファイルをそのままモジュールに追加する。
基本構文
umbrella header "ヘッダーファイル"
umblrella header宣言の例
module SDK {
umbrella header "SDK.h"
}
SDK.hでinculdeされているheaderファイルが全てSDKモジュールに追加される。
1-3-5 submodule宣言
submodule declaration
機能
モジュール宣言内部にモジュールを宣言する。
explicitオプションなどが使用できるようなり、
Objective-Cの場合は、明示的に「.」演算子を用いてサブモジュールのみ
importを行うことが出来る。
基本構文
モジュール宣言と同じ。
submodule宣言の例
module MyLib {
/*サブモジュールSub1の宣言*/
module Sub1 {
header "a.h"
}
/*サブモジュールSub2の宣言*/
module Sub2 {
header "b.h"
}
/*explicitオプションが使えるのはサブモジュールのみ*/
explicit Sub3 {
heaser "c.h"
}
}
Objective-Cにおけるサブモジュールの明示的importの例
@import MyLib.Sub1;
explicitオプション
サブモジュールに対してのみ指定することが出来る。
上記指定することで、ルートモジュールが**@import**でインポートされても
explicit指定しているサブモジュールはインポートされない。
explicitサブモジュールを使用するためには、直接モジュールIDを指定してインポートするか
他のモジュールにてexportされている時のみである。
上記の例では、
@import MyLib;
でMyLibモジュールをインポートしてもモジュールSub3はインポートされない。
1-3-6 暗黙的submodule宣言
inferred submodule declaration
機能
submodule宣言において、module-idに ワイルドカード (*)を使用することで、
umbrella宣言で追加したヘッダーファイルをそのまま同じ名前でモジュール宣言を行う。
基本構文
module トップモジュール {
umbrella宣言
module * {
module-member
}
}
```
> umbrella宣言はdirectory, headerどちらでも構わない。
>
#### 暗黙的submodule宣言の例
module MyLib {
umbrella header "sdk.h"
explicit module * { }
}
> 上記では
> sdk.hでincludeされている各ヘッダーファイル名がそのままモジュールIDとなり、
> モジュールが自動的に宣言される。
例えば上記の例にて
sdk.hで以下のヘッダーファイルがincludeされていた場合、
#include "a.h"
#include "b.h"
上記は以下と同じことになる。
module SDK {
explicit module a {
header "a.h"
}
explicit module b {
header "b.h"
}
}
### 1-3-7 export宣言について
*export declaration*
#### 機能
該当モジュールがimportされた場合は、exportで指定したモジュールも
一緒にimportする。
#### 基本構文
expoert 他モジュール名
#### ヘッダーをモジュール化することによる問題
モジュール間同士は互いに独立しているため、
各ヘッダーを別々のモジュールに定義すると、リンクエラーが発生する可能性がある。
以下例
```const.h
const int CONST_VAL = 10;
#include "const.h"
#include <stdio.h>
#include "a.h"
int main(void)
{
printf("VAL = %d", CONST_VAL);
return 0;
}
/*
const.hファイルをモジュールCに
a.hファイルをモジュールAに変換する。
*/
module A {
header "a.h"
}
module C {
header "const.h"
}
上記の実行結果
clang -fmodules -o main main.c
モジュールCをインポートせよというエラーが出ている。
export宣言の役割
そこで以下のようにexport宣言を行うことで、
モジュールAをインポートした場合、モジュールCもインポートすることが出来る。
module A {
header "a.h"
export C
}
module C {
header "const.h"
}
上記では明示的にモジュールCをexportしているが、
以下のように*を使用することで、
a.hファイルでincludeされているヘッダーファイルのモジュールを
自動的にインポートする。
module A {
header "a.h"
export *
}
module C {
header "const.h"
}
#2. モジュール化することによって解決する問題
既存のheaderのincludeの問題とモジュール化することによって解決すること解決しないことにつて
2-1 headerのincludeが抱えている問題
- コンパイル時間の拡大性
コンパイラーはヘッダーがincludeされるたびに前処理とテキスト解析を行う。
従ってheaderファイルがM個あり、それぞれN個のファイルにincludeされていた場合、上記のinclude処理数は M x Nで拡大していく。
- 脆さ
- マクロ宣言による他ソースファイルの破壊
- マクロ宣言のコンフリクト
- マクロ宣言のundef命令における強制的な削除
- 慣例的な回避策
- とてつもなく長いマクロ名(大文字)
- __アンダースコア名はシステムによる予約名とされていること
- ツールのビルドの難しさ
- ライブラリー同士の境界がはっきりしていない。
- c, C++, Objective-Cどの言語のヘッダーか分からない。
2-2 モジュールによって解決されること
- コンパイル時間の拡大性
モジュールのimport処理の場合は、pchファイルと同じようにヘッダーファイルを1回コンパイルしてバイナリー化するため、
処理はヘッダーのコンパイル処理とモジュールの組み込み処理に分解することが出来、
処理数はM + Nへと減らすことが出来る。
- 脆さ
モジュール間同士は独立して、影響を及ぼさない。
- ツールのビルドの難しさ
モジュールはどの言語に所属しているヘッダーファイルか記述することが出来る。
#3 モジュールのインポートは自動的に行われる。
Objective-Cの場合は、フレームワークのリンクを追加する必要がない。