Improve this page
Github へのログインが必要です。
簡単な修正は、ここから fork、オンライン編集、pull request ができます。
大きな修正については、
通常の clone で行って下さい。
Page wiki
関連するWikiページを参照・編集
English
このページの英語版(原文)
テンプレート・ミックスイン
A TemplateMixin は、 任意の種類の宣言をテンプレートから取り出し、 現在のスコープへ導入します。TemplateMixinDeclaration:
mixin template TemplateIdentifier ( TemplateParameterList ) Constraintopt
{ DeclDefs }
TemplateMixin:
mixin TemplateIdentifier ;
mixin TemplateIdentifier MixinIdentifier ;
mixin TemplateIdentifier !( TemplateArgumentList ) ;
mixin TemplateIdentifier !( TemplateArgumentList ) MixinIdentifier ;
MixinIdentifier:
Identifier
TemplateMixin は、モジュール、クラス、構造体、共用体の宣言リストの途中や、 あるいは一つの文としての位置に書くことができます。 TemplateIdentifier は TemplateDeclaration を参照します。 もしテンプレートが引数を取らないのであれば、 !(TemplateArgumentList) を省略した形式で書いても構いません。
テンプレートのインスタンス化とは異なり、テンプレートのmixinの本体は、 テンプレート定義のあるスコープではなく、mixinされた先のスコープの中で評価されます。 これは、テンプレートの本体をカット&ペーストして mixin したい場所に貼り付けるのと似た動作と言えるでしょう。 この機能は、パラメタ抽象された ‘boilerplate’ コードを挿入するのにはもちろん、 ただのインスタンス化では不可能な、 テンプレートネスト関数の作成にも役立ちます。
mixin template Foo() {
int x = 5;
}
mixin Foo;
struct Bar {
mixin Foo;
}
void test() {
writefln("x = %d", x); // 5 を表示
{
Bar b;
int x = 3;
writefln("b.x = %d", b.x); // 5 を表示
writefln("x = %d", x); // 3 を表示
{
mixin Foo;
writefln("x = %d", x); // 5 を表示
x = 4;
writefln("x = %d", x); // 4 を表示
}
writefln("x = %d", x); // 3 を表示
}
writefln("x = %d", x); // 5 を表示
}
Mixinはパラメタ化することができます:
mixin template Foo(T) {
T x = 5;
}
mixin Foo!(int); // int型変数xを作成
クラスに仮想関数を付け加えることも可能です:
mixin template Foo() {
void func() { writefln("Foo.func()"); }
}
class Bar {
mixin Foo;
}
class Code : Bar {
override void func() { writefln("Code.func()"); }
}
void test() {
Bar b = new Bar();
b.func(); // calls Foo.func()
b = new Code();
b.func(); // calls Code.func()
}
Mixin は、テンプレート宣言のあるスコープではなく、
mixin が現れたスコープにおいて評価されます:
int y = 3;
mixin template Foo() {
int abc() { return y; }
}
void test() {
int y = 8;
mixin Foo; // ローカルの y が選ばれる。グローバルの y ではなく。
assert(abc() == 8);
}
alias引数を使うと、シンボルをパラメタに取ることができます:
mixin template Foo(alias b) {
int abc() { return b; }
}
void test() {
int y = 8;
mixin Foo!(y);
assert(abc() == 8);
}
以下の例は、任意の文に対して使える汎用の"ダフのデバイス"を実装するのに
Mixin を使っています。(この例の場合、任意の文にできる箇所を太字で表記しています。)
ネストした関数もdelegateリテラルで同様に生成することができますし、
これらはコンパイラによるインライン化が見込めます:
mixin template duffs_device(alias id1, alias id2, alias s)
{
void duff_loop()
{
if (id1 < id2)
{
typeof(id1) n = (id2 - id1 + 7) / 8;
switch ((id2 - id1) % 8)
{
case 0: do { s(); goto case;
case 7: s(); goto case;
case 6: s(); goto case;
case 5: s(); goto case;
case 4: s(); goto case;
case 3: s(); goto case;
case 2: s(); goto case;
case 1: s(); continue;
default: assert(0, "Impossible");
} while (--n > 0);
}
}
}
}
void foo() { writefln("foo"); }
void test() {
int i = 1;
int j = 11;
mixin duffs_device!(i, j, delegate { foo(); } );
duff_loop(); // foo() を10回実行
}
ミックスインのスコープ
Mixinの中の宣言は、周囲のスコープに ‘import’ されます。 Mixinの中の宣言と同じ名前の宣言が周囲のスコープに存在した場合、 周囲のスコープの宣言が、 Mixinのそれを上書きします:int x = 3;
mixin template Foo() {
int x = 5;
int y = 5;
}
mixin Foo;
int y = 3;
void test() {
writefln("x = %d", x); // 3 を表示
writefln("y = %d", y); // 3 を表示
}
二つの異なるMixinが同じスコープへ置かれて、
しかもおのおのが同名の宣言を行っている場合、
その宣言を参照すると曖昧さのためエラーになります:
mixin template Foo() {
int x = 5;
void func(int x) { }
}
mixin template Bar() {
int x = 4;
void func() { }
}
mixin Foo;
mixin Bar;
void test() {
writefln("x = %d", x); // エラー、x は曖昧
func(); // エラー、func は曖昧
}
この func() の呼び出しが曖昧とされるのは、 Foo.func と Bar.func が異なるスコープにあるからです。
mixin 宣言に MixinIdentifier が指定されていれば、 それを使って曖昧さを解消できます:
int x = 6;
mixin template Foo() {
int x = 5;
int y = 7;
void func() { }
}
mixin template Bar() {
int x = 4;
void func() { }
}
mixin Foo F;
mixin Bar B;
void test() {
writefln("y = %d", y); // 7 を表示
writefln("x = %d", x); // 6 を表示
writefln("F.x = %d", F.x); // 5 を表示
writefln("B.x = %d", B.x); // 4 を表示
F.func(); // Foo.func を呼ぶ
B.func(); // Bar.func を呼ぶ
}
alias宣言を使って、 違うmixinで宣言された関数をオーバーロードすることは可能です:
mixin template Foo() {
void func(int x) { }
}
mixin template Bar() {
void func() { }
}
mixin Foo!() F;
mixin Bar!() B;
alias F.func func;
alias B.func func;
void main() {
func(); // B.func を呼び出す
func(1); // F.func を呼び出す
}
宣言は周囲のもので上書きされますが、 mixinの内部では、それ自身の独自のスコープを持ちます。
int x = 4;
mixin template Foo() {
int x = 5;
int bar() { return x; }
}
mixin Foo;
void test() {
writefln("x = %d", x); // 4 を表示
writefln("bar() = %d", bar()); // 5 を表示
}