www.digitalmars.com
Last update Wed May 31 17:11:57 2006

Mixins

Mixins quer dizer coisas diferentes em linguagens de programação diferentes. Em D, um mixin é um conjunto de declarações arbitrario do corpo de uma TemplateDeclaration e inserí-las no contexto atual.
TemplateMixin:
mixin TemplateIdentifier ;
mixin TemplateIdentifier MixinIdentifier ;
mixin TemplateIdentifier !() TemplateArgumentList ;
mixin TemplateIdentifier !() TemplateArgumentList MixinIdentifier ;
MixinIdentifier:
Identifier
Um TemplateMixin pode ocorrer em listas de declaração de módulos, classes, uniões e como uma instrução. O TemplateIdentifier se refere à uma TemplateDeclaration. Se a TemplateDeclaration não tem parâmetros, a forma do mixin que não tem !(TemplateArgumentList) pode ser usada.

Diferentemente de uma instanciação de gabarito, um corpo de um mixin é avaliado dentro do escopo onde o mixin aparece, não onde a declaração do gabarito está definida. Isso é anáçogo à recortar e colar o corpo do gabarito no lugar do mixin. Isso é útil para injetar código parametrizado, bem como para crear funções aninhadas gabaritadas, que não são possíveis com instanciações de gabaritos.

template Foo()
{
int x = 5;
}

mixin Foo;
struct Bar
{
mixin Foo;
}
void test()
{
printf("x = %d\n", x); // imprime 5
{ Bar b;
int x = 3;

printf("b.x = %d\n", b.x); // imprime 5
printf("x = %d\n", x); // imprime 3
{
mixin Foo;
printf("x = %d\n", x); // imprime 5
x = 4;
printf("x = %d\n", x); // imprime 4
}
printf("x = %d\n", x); // imprime 3
}
printf("x = %d\n", x); // imprime 5
}
Mixins podem ser parametrizados:
template Foo(T)
{
T x = 5;
}

mixin Foo!(int); // cria x de tipo int
Mixins podem adicionar funções virtuais à uma classe:
template Foo()
{
void func() { printf("Foo.func()\n"); }
}

class Bar
{
mixin Foo;
}

class Code : Bar
{
void func() { printf("Code.func()\n"); }
}

void test()
{
Bar b = new Bar();
b.func(); // chama Foo.func()

b = new Code();
b.func(); // chama Code.func()
}
Mixins são avaliados no escopo onde aparecem, não no escopo da declaração do gabarito:
int y = 3;

template Foo()
{
int abc() { return y; }
}

void test()
{
int y = 8;
mixin Foo; // y local é escolhido, não o y global
assert(abc() == 8);
}
Mixins podem parametrizar simbolos usando parametros apelido:
template Foo(alias b)
{
int abc() { return b; }
}

void test()
{
int y = 8;
mixin Foo!(y);
assert(abc() == 8);
}
Esse exemplo usa um mixin para implementar um dispositivo Duff's genérico para uma instrução arbitrária (nesse caso, a instrução arbitraria está em negrito). Uma função aninhada é gerada, bem como um delegado literal, estes podem ser tornados inline pelo compilador:
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();
case 7: s();
case 6: s();
case 5: s();
case 4: s();
case 3: s();
case 2: s();
case 1: s();
} while (--n > 0);
}
}
}
}

void foo() { printf("foo\n"); }

void test()
{
int i = 1;
int j = 11;

mixin duffs_device!(i, j, delegate { foo(); } );
duff_loop(); // executa foo() 10 vezes
}

Escopo do Mixin

As declarações em um mixin são 'importadas" no escopo envolvendo-o. Se o nome de uma declaraç~´ao em um mixin é o mesmo de uma declaração no escopo envolvendo-o, a declaração envolvendo anula a do mixin:
int x = 3;

template Foo()
{
int x = 5;
int y = 5;
}

mixin Foo;
int y = 3;

void test()
{
printf("x = %d\n", x); // imprime 3
printf("y = %d\n", y); // imprime 3
}
Se dois mixins diferentes ão colocados no mesmo escopo, e cada um define uma declaração com o mesmo nome, há um erro de ambigüidade quando a declaração é referenciada:
template Foo()
{
int x = 5;
}

template Bar()
{
int x = 4;
}

mixin Foo;
mixin Bar;
void test()
{
printf("x = %d\n", x); // erro, x é ambiguo
}
Se um mixin tem um MixinIdentifier, ele pode ser usado para diferenciar:
int x = 6;

template Foo()
{
int x = 5;
int y = 7;
}

template Bar()
{
int x = 4;
}

mixin Foo F;
mixin Bar B;
void test()
{
printf("y = %d\n", y); // imprime 7
printf("x = %d\n", x); // imprime 6
printf("F.x = %d\n", F.x); // imprime 5
printf("B.x = %d\n", B.x); // imprime 4
}
Um mixin tem seu próprio escopo, mesmo se uma declaração é anulada pela incluindo:
int x = 4;

template Foo()
{
int x = 5;
int bar() { return x; }
}

mixin Foo;
void test()
{
printf("x = %d\n", x); // imprime 4
printf("bar() = %d\n", bar()); // imprime 5
}