$(DDOC $(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(SPEC_S Template Mixins,
$(DDOC_BLANKLINE )
$(HEADERNAV_TOC $(HEADERNAV_SUBITEMS parameters, Mixin Parameters,
$(HEADERNAV_ITEM example, Example)
)
$(HEADERNAV_SUBITEMS mixin_scope, Mixin Scope,
$(HEADERNAV_ITEM resolving_ambiguities, Resolving Ambiguities)
)
$(HEADERNAV_SUBITEMS aggregate_types, Aggregate Type Mixins,
$(HEADERNAV_ITEM virtual_functions, Mixin Virtual Functions)
$(HEADERNAV_ITEM destructors, Mixin Destructors)
)
)
$(DDOC_BLANKLINE )
$(P A $(I TemplateMixin) takes an arbitrary set of declarations from
the body of a $(GLINK2 template, TemplateDeclaration) and inserts them
into the current context.)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME TemplateMixinDeclaration):
$(D mixin) $(D template) $(GLINK_LEX Identifier) $(GLINK2 template, TemplateParameters) $(GLINK2 template, Constraint)$(OPT ) $(D {) $(GLINK2 module, DeclDefs)$(OPT ) $(D })
$(DDOC_BLANKLINE )
$(GNAME TemplateMixin):
$(D mixin) $(GLINK MixinTemplateName) $(GLINK2 template, TemplateArguments)$(OPT ) $(GLINK_LEX Identifier)$(OPT ) $(D ;)
$(DDOC_BLANKLINE )
$(GNAME MixinTemplateName):
$(D .) $(GLINK MixinQualifiedIdentifier)
$(GLINK MixinQualifiedIdentifier)
$(GLINK2 type, Typeof) $(D .) $(GLINK MixinQualifiedIdentifier)
$(DDOC_BLANKLINE )
$(GNAME MixinQualifiedIdentifier):
$(GLINK_LEX Identifier)
$(GLINK_LEX Identifier) $(D .) $(GSELF MixinQualifiedIdentifier)
$(GLINK2 template, TemplateInstance) $(D .) $(GSELF MixinQualifiedIdentifier)
)
$(DDOC_BLANKLINE )
$(P A $(I TemplateMixin) can occur in declaration lists of modules,
classes, structs, unions, or as a statement.
$(I MixinTemplateName) must refer to a $(I TemplateDeclaration) or
$(I TemplateMixinDeclaration).
If the $(I TemplateDeclaration) requires no parameters, $(I TemplateArguments)
can be omitted.
)
$(DDOC_BLANKLINE )
$(P Unlike a $(DDSUBLINK spec/template, instantiation_scope, template instantiation),
a template mixin's body is evaluated
within the scope where the mixin appears, not where the template declaration
is defined. It is analogous to cutting and pasting the body of
the template into the location of the mixin into a $(LINK2 #mixin_scope, nested scope). It is useful for injecting
parameterized $(SINGLEQUOTE boilerplate) code, as well as for creating
templated nested functions, which is not always possible with
template instantiations.
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int) y = 3;
$(D_KEYWORD mixin) $(D_KEYWORD template) Foo()
{
$(D_KEYWORD int) abc() { $(D_KEYWORD return) y; }
}
$(D_KEYWORD void) test()
{
$(D_KEYWORD int) y = 8;
$(D_KEYWORD mixin) Foo; $(D_COMMENT // local y is picked up, not global y
) $(D_KEYWORD assert)(abc() == 8);
}
)
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD mixin) $(D_KEYWORD template) Foo()
{
$(D_KEYWORD int) x = 5;
}
$(D_KEYWORD mixin) Foo;
$(D_KEYWORD struct) Bar
{
$(D_KEYWORD mixin) Foo;
}
$(D_KEYWORD void) main()
{
writeln($(D_STRING "x = "), x); $(D_COMMENT // prints 5
) {
Bar b;
$(D_KEYWORD int) x = 3;
writeln($(D_STRING "b.x = "), b.x); $(D_COMMENT // prints 5
) writeln($(D_STRING "x = "), x); $(D_COMMENT // prints 3
) {
$(D_KEYWORD mixin) Foo;
writeln($(D_STRING "x = "), x); $(D_COMMENT // prints 5
) x = 4;
writeln($(D_STRING "x = "), x); $(D_COMMENT // prints 4
) }
writeln($(D_STRING "x = "), x); $(D_COMMENT // prints 3
) }
writeln($(D_STRING "x = "), x); $(D_COMMENT // prints 5
)}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 parameters, Mixin Parameters)
$(DDOC_BLANKLINE )
$(P Mixins can be
$(DDSUBLINK spec/template, parameters, parameterized):)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD mixin) $(D_KEYWORD template) Foo(T)
{
T x = 5;
}
$(D_KEYWORD mixin) Foo!($(D_KEYWORD int)); $(D_COMMENT // create x of type int
))
$(DDOC_BLANKLINE )
$(P Mixins can parameterize symbols using
$(DDSUBLINK spec/template, aliasparameters, alias parameters):)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD mixin) $(D_KEYWORD template) Foo($(D_KEYWORD alias) b)
{
$(D_KEYWORD int) abc() { $(D_KEYWORD return) b; }
}
$(D_KEYWORD void) test()
{
$(D_KEYWORD int) y = 8;
$(D_KEYWORD mixin) Foo!(y);
$(D_KEYWORD assert)(abc() == 8);
}
)
$(DDOC_BLANKLINE )
$(LNAME2 example, Example)
$(DDOC_BLANKLINE )
$(P This example uses a mixin to implement a generic Duff's device
for an arbitrary statement (in this case, the arbitrary statement
is in bold). A nested function is generated as well as a
delegate literal, these can be inlined by the compiler:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD mixin) $(D_KEYWORD template) duffs_device($(D_KEYWORD alias) low, $(D_KEYWORD alias) high, $(D_KEYWORD alias) fun)
{
$(D_KEYWORD void) duff_loop()
{
$(D_KEYWORD if) (low < high)
{
$(D_KEYWORD auto) n = (high - low + 7) / 8;
$(D_KEYWORD switch) ((high - low) % 8)
{
$(D_KEYWORD case) 0: $(D_KEYWORD do) { fun(); $(D_KEYWORD goto) $(D_KEYWORD case);
$(D_KEYWORD case) 7: fun(); $(D_KEYWORD goto) $(D_KEYWORD case);
$(D_KEYWORD case) 6: fun(); $(D_KEYWORD goto) $(D_KEYWORD case);
$(D_KEYWORD case) 5: fun(); $(D_KEYWORD goto) $(D_KEYWORD case);
$(D_KEYWORD case) 4: fun(); $(D_KEYWORD goto) $(D_KEYWORD case);
$(D_KEYWORD case) 3: fun(); $(D_KEYWORD goto) $(D_KEYWORD case);
$(D_KEYWORD case) 2: fun(); $(D_KEYWORD goto) $(D_KEYWORD case);
$(D_KEYWORD case) 1: fun(); $(D_KEYWORD continue);
$(D_KEYWORD default): $(D_KEYWORD assert)(0, $(D_STRING "Impossible"));
} $(D_KEYWORD while) (--n > 0);
}
}
}
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) i = 1;
$(D_KEYWORD int) j = 11;
$(D_KEYWORD mixin) duffs_device!(i, j, $(D_KEYWORD delegate) { writeln($(D_STRING "foo")); });
duff_loop(); $(D_COMMENT // executes foo$(LPAREN)$(RPAREN ) 10 times
)}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 mixin_scope, Mixin Scope)
$(DDOC_BLANKLINE )
$(P The declarations in a mixin are placed in a nested scope and then
$(SINGLEQUOTE imported) into the surrounding
scope. If the name of a declaration in a mixin is the same
as a declaration in the surrounding scope, the surrounding declaration
overrides the mixin one:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD int) x = 3;
$(D_KEYWORD mixin) $(D_KEYWORD template) Foo()
{
$(D_KEYWORD int) x = 5;
$(D_KEYWORD int) y = 5;
}
$(D_KEYWORD mixin) Foo;
$(D_KEYWORD int) y = 3;
$(D_KEYWORD void) main()
{
writeln($(D_STRING "x = "), x); $(D_COMMENT // prints 3
) writeln($(D_STRING "y = "), y); $(D_COMMENT // prints 3
)}
)
)
$(DDOC_BLANKLINE )
$(P A mixin has its own scope, even if a declaration is overridden
by the enclosing one:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD int) x = 4;
$(D_KEYWORD mixin) $(D_KEYWORD template) Foo()
{
$(D_KEYWORD int) x = 5;
$(D_KEYWORD int) bar() { $(D_KEYWORD return) x; }
}
$(D_KEYWORD mixin) Foo;
$(D_KEYWORD void) main()
{
writeln($(D_STRING "x = "), x); $(D_COMMENT // prints 4
) writeln($(D_STRING "bar$(LPAREN)$(RPAREN ) = "), bar()); $(D_COMMENT // prints 5
)}
)
)
$(DDOC_BLANKLINE )
$(P If two different mixins are put in the same scope, and each
define a declaration with the same name, there is an ambiguity
error when the declaration is referenced:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD mixin) $(D_KEYWORD template) Foo()
{
$(D_KEYWORD int) x = 5;
$(D_KEYWORD void) func($(D_KEYWORD int) x) { }
}
$(D_KEYWORD mixin) $(D_KEYWORD template) Bar()
{
$(D_KEYWORD int) x = 4;
$(D_KEYWORD void) func($(D_KEYWORD long) x) { }
}
$(D_KEYWORD mixin) Foo;
$(D_KEYWORD mixin) Bar;
$(D_KEYWORD void) main()
{
writeln($(D_STRING "x = "), x); $(D_COMMENT // error, x is ambiguous
) func(1); $(D_COMMENT // error, func is ambiguous
)}
)
)
$(P The call to $(D func()) is ambiguous because
Foo.func
and Bar.func
are in different scopes.
)
$(DDOC_BLANKLINE )
$(LNAME2 resolving_ambiguities, Resolving Ambiguities)
$(DDOC_BLANKLINE )
$(P If a mixin has an $(I Identifier), it can be used to
disambiguate between conflicting symbols:
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD int) x = 6;
$(D_KEYWORD mixin) $(D_KEYWORD template) Foo()
{
$(D_KEYWORD int) x = 5;
$(D_KEYWORD int) y = 7;
$(D_KEYWORD void) func() { }
}
$(D_KEYWORD mixin) $(D_KEYWORD template) Bar()
{
$(D_KEYWORD int) x = 4;
$(D_KEYWORD void) func() { }
}
$(D_KEYWORD mixin) Foo F;
$(D_KEYWORD mixin) Bar B;
$(D_KEYWORD void) main()
{
writeln($(D_STRING "y = "), y); $(D_COMMENT // prints 7
) writeln($(D_STRING "x = "), x); $(D_COMMENT // prints 6
) writeln($(D_STRING "F.x = "), F.x); $(D_COMMENT // prints 5
) writeln($(D_STRING "B.x = "), B.x); $(D_COMMENT // prints 4
) F.func(); $(D_COMMENT // calls Foo.func
) B.func(); $(D_COMMENT // calls Bar.func
)}
)
)
$(P Alias declarations can be used to form an
$(DDSUBLINK spec/function, overload-sets, overload set) of
functions declared in different mixins:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD mixin) $(D_KEYWORD template) Foo()
{
$(D_KEYWORD void) func($(D_KEYWORD int) x) { }
}
$(D_KEYWORD mixin) $(D_KEYWORD template) Bar()
{
$(D_KEYWORD void) func($(D_KEYWORD long) x) { }
}
$(D_KEYWORD mixin) Foo!() F;
$(D_KEYWORD mixin) Bar!() B;
$(D_KEYWORD alias) func = F.func;
$(D_KEYWORD alias) func = B.func;
$(D_KEYWORD void) main()
{
func(1); $(D_COMMENT // calls B.func
) func(1L); $(D_COMMENT // calls F.func
)}
)
$(DDOC_BLANKLINE )
$(LNAME2 aggregate_types, Aggregate Type Mixins)
$(DDOC_BLANKLINE )
$(LNAME2 virtual_functions, Mixin Virtual Functions)
$(DDOC_BLANKLINE )
$(P Mixins can add virtual functions to a class:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD mixin) $(D_KEYWORD template) Foo()
{
$(D_KEYWORD void) func() { writeln($(D_STRING "Foo.func$(LPAREN)$(RPAREN )")); }
}
$(D_KEYWORD class) Bar
{
$(D_KEYWORD mixin) Foo;
}
$(D_KEYWORD class) Code : Bar
{
$(D_KEYWORD override) $(D_KEYWORD void) func() { writeln($(D_STRING "Code.func$(LPAREN)$(RPAREN )")); }
}
$(D_KEYWORD void) main()
{
Bar b = $(D_KEYWORD new) Bar();
b.func(); $(D_COMMENT // calls Foo.func$(LPAREN)$(RPAREN )
)
b = $(D_KEYWORD new) Code();
b.func(); $(D_COMMENT // calls Code.func$(LPAREN)$(RPAREN )
)}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 destructors, Mixin Destructors)
$(DDOC_BLANKLINE )
$(P An aggregate type can mixin additional destructors.
Destructors are run in the opposite order to declaration order.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio;
$(D_KEYWORD mixin) $(D_KEYWORD template) addNewDtor()
{
~$(D_KEYWORD this)()
{
writeln($(D_STRING "Mixin dtor"));
}
}
$(D_KEYWORD struct) S
{
~$(D_KEYWORD this)()
{
writeln($(D_STRING "Struct dtor"));
}
$(D_KEYWORD mixin) addNewDtor;
}
$(D_KEYWORD void) main()
{
S s;
$(D_COMMENT // prints `Mixin dtor`
) $(D_COMMENT // prints `Struct dtor`
)}
)
)
$(DDOC_BLANKLINE )
$(SPEC_SUBNAV_PREV_NEXT template, Templates, contracts, Contract Programming)
)
)