LoginSignup
46

More than 5 years have passed since last update.

Clang Modulesについて

Last updated at Posted at 2015-02-24

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

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

0.Clang Modulesの機能について

  1. ヘッダーをコンパイルし、バイナリー表現のモジュールに変換する。
  2. 1度モジュールに変換したヘッダーは、変更しない限り再度コンパイルされない。
  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ファイルに、
モジュール宣言を行うことでヘッダーをモジュール化する。
また複数のヘッダーファイルを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.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

モジュール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の場合は、フレームワークのリンクを追加する必要がない。

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
46