Clang Modulesについて


ClangにModulesという機能を知り、公式リファレンスを要約いたしました。

公式リファレンス
http://clang.llvm.org/docs/Modules.html

0.Clang Modulesの機能について
----------------------------

1. ヘッダーをコンパイルし、バイナリー表現のモジュールに変換する。
2. １度モジュールに変換したヘッダーは、変更しない限り再度コンパイルされない。
3. モジュールに変換したヘッダーは、includeされると自動的にモジュールがimportされる。
4. モジュール同士は独立しているため、マクロがincludeされることによるソースコードの破壊を防ぐことが出来る。
5. Objective-Cの場合は、明示的にモジュールを以下のようにインポートできる。

   ```
   @import Foundation;
   @import AVFoundation; //AVFoundation.framworkのリンクを追加する必要がない。
   ```

   > Note 
   > Xcode 6からは自動的にModulesが有効となっているため、
   > \#import \<Foundation/Foundation.h\>　は自動的に裏で＠import Foundation;と書き換えられている。
   > ゆえにフレームワークのリンクの追加が必要となくなった。


#1. ヘッダーをモジュール化する
--------------------------------------------

## 1-1 Clang Modulesを使用する方法

1. **module.modulemap**ファイルを作成し、ヘッダーファイルと一緒に配置する。
2. **-fmodules**オプションをつけてコンパイルする。

```
$ clang -fmodules main.c -o main.o
```
> 上記のようにソースコード側でモジュールを使用するための記述は一切いらないため
> 他のコンパイラで使用されるコード

## 1-2 module.modulemapファイルとモジュールマップ言語

ヘッダーをモジュール化するには、
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ファイルに、
>モジュール宣言を行うことでヘッダーをモジュール化する。
>また複数のヘッダーファイルを１つのモジュールへと変換することも可能。
>Clangは、モジュールに変換されたヘッダーファイルが*include*されると自動的に該当するモジュールをimportする。


## 1-3 モジュールマップ言語

### 1-3-1 モジュール宣言

#### 基本構文

```
[explictｵﾌﾟｼｮﾝ] [frameworkｵﾌﾟｼｮﾝ] module module-id [属性] {
    /*モジュールメンバ*/
}
 
```
> **module-id**はユニーク名である必要がある。
> **モジュールメンバ**には以下の宣言が行える。
>
>  * **header**
>  * **umbrella directory**
>  * **umbrella header**
>  * **submodule**
>  * **export**
>  * **Use**


#### モジュール宣言の例

```module.modulemap

module Hello {
   //コメントも可能
   header "hello.h"
   export *
}
```
> hello.hファイルをモジュールHelloへと変換する。

### 1-3-2 header宣言 

*header declaration*

#### 機能

該当モジュールにヘッダーファイルを変換する。

#### 基本構文

```
header "ヘッダーファイル"
```

#### hedaer宣言の例

```module.modulemap
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の例

```main.m
@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; 
```

```a.h
#include "const.h"
```

```main.c
#include  <stdio.h>
#include "a.h"

int main(void)
{
    printf("VAL = %d", CONST_VAL);
    return 0;
}
```

```module.modulemap
/*
const.hファイルをモジュールCに
a.hファイルをモジュールAに変換する。
*/
module A {
    header "a.h"
}                
module C {
    header "const.h"
}
```

上記の実行結果

```
clang -fmodules -o main main.c
```

![スクリーンショット 2015-03-14 18.53.48.png](https://qiita-image-store.s3.amazonaws.com/0/70065/2875d0ac-6661-0db0-b1f8-dc41e2ec67e0.png)
> モジュール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ファイルと同じようにヘッダーファイルを１回コンパイルしてバイナリー化するため、
> 処理はヘッダーのコンパイル処理とモジュールの組み込み処理に分解することが出来、
> 処理数は*M + N*へと減らすことが出来る。

* 脆さ

> モジュール間同士は独立して、影響を及ぼさない。

* ツールのビルドの難しさ

> モジュールはどの言語に所属しているヘッダーファイルか記述することが出来る。


#3 モジュールのインポートは自動的に行われる。

Objective-Cの場合は、フレームワークのリンクを追加する必要がない。