Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 28 additions & 47 deletions struct.dd
Original file line number Diff line number Diff line change
Expand Up @@ -544,63 +544,44 @@ $(SECTION3 $(LNAME2 AssignOverload, Assignment Overload),

$(H2 $(LNAME2 nested, Nested Structs))

$(P A $(I nested struct) is a struct that is declared inside the scope
$(P A $(I nested struct) is a struct that is declared inside the scope
of a function or a templated struct that has aliases to local
functions as a template argument.
Nested structs have member functions.
It has access to the context of its enclosing scope
(via an added hidden field).
)

---
void foo() {
int i = 7;
struct SS {
int x,y;
int bar() { return x + i + 1; }
}
SS s;
s.x = 3;
s.bar(); // returns 11
}
---
---
void foo() {
int i = 7;
struct SS {
int x,y;
int bar() { return x + i + 1; }
}
SS s;
s.x = 3;
s.bar(); // returns 11
}
---
)

$(P A struct can be prevented from being nested by
$(P A struct can be prevented from being nested by
using the static attribute, but then of course it
will not be able to access variables from its enclosing
scope.)

---
void foo() {
int i = 7;
static struct SS {
int x,y;
int bar() {
return i; // error, SS is not a nested struct
}
}
}
---

$(P A templated struct can become a nested struct if it
has a local function passed as an aliased argument:
)

---
struct A(alias F) {
int fun(int i) { return F(i); }
}

A!(F) makeA(alias F)() {return A!(F)(); }

void main() {
int x = 40;
int fun(int i) { return x + i; }
A!(fun) a = makeA!(fun)();
a.fun(2);
}
---
scope.

---
void foo() {
int i = 7;
static struct SS {
int x,y;
int bar() {
return i; // error, SS is not a nested struct
}
}
}
---
)

)

Expand Down
195 changes: 195 additions & 0 deletions template.dd
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,201 @@ $(H3 $(LNAME2 auto-ref-parameters, Function Templates with Auto Ref Parameters))
---
)

$(H2 $(LNAME2 nested-templates, Nested Templates))

$(P If a template is declared in aggregate or function local scope, the
instantiated functions will implicitly capture the context of the
enclosing scope.

----
class C
{
int num;

this(int n) { num = n; }

template Foo()
{
// 'foo' can access 'this' reference of class C object.
void foo(int n) { this.num = n; }
}
}

void main()
{
auto c = new C(1);
assert(c.num == 1);

c.Foo!().foo(5);
assert(c.num == 5);

template Bar()
{
// 'bar' can access local variable of 'main' function.
void bar(int n) { c.num = n; }
}
Bar!().bar(10);
assert(c.num == 10);
}
----
)

$(P Above, $(D Foo!().foo) will work just the same as a member function
of class $(D C), and $(D Bar!().bar) will work just the same as a nested
function within function $(D main$(LPAREN)$(RPAREN)).)

$(P If a template has a $(RELATIVE_LINK2 aliasparameters, template alias parameter),
and is instantiated with a local symbol, the instantiated function will
implicitly become nested in order to access runtime data of the given
local symbol.

----
template Foo(alias sym)
{
void foo() { sym = 10; }
}

class C
{
int num;

this(int n) { num = n; }

void main()
{
assert(this.num == 1);

alias fooX = Foo!(C.num).foo;

// fooX will become member function implicitly, so &fooX returns delegate object.
static assert(is(typeof(&fooX) == delegate));

fooX(); // called by using valid 'this' reference.
assert(this.num == 10); // OK
}
}

void main()
{
new C(1).main();

int num;
alias fooX = Foo!num.foo;

// fooX will become nested function implicitly, so &fooX returns delegate object.
static assert(is(typeof(&fooX) == delegate));

fooX();
assert(num == 10); // OK
}
----
)

$(P Not only functions, but also instantiated class and struct types can
become nested via implicitly captured context.

----
class C
{
int num;
this(int n) { num = n; }

class N(T)
{
// instantiated class N!T can become nested in C
T foo() { return num * 2; }
}
}

void main()
{
auto c = new C(10);
auto n = c.new N!int();
assert(n.foo() == 20);
}
----

----
void main()
{
int num = 10;
struct S(T)
{
// instantiated struct S!T can become nested in main()
T foo() { return num * 2; }
}
S!int s;
assert(s.foo() == 20);
}
----
)

$(P A templated $(D struct) can become a nested $(D struct) if it
is instantiated with a local symbol passed as an aliased argument:

---
struct A(alias F)
{
int fun(int i) { return F(i); }
}

A!F makeA(alias F)() { return A!F(); }

void main()
{
int x = 40;
int fun(int i) { return x + i; }
A!fun a = makeA!fun();
assert(a.fun(2) == 42);
}
---
)

$(H3 Limitation:)

$(P Currently nested templates can capture at most one context. As a typical
example, non-static template member functions cannot take local symbol
by using template alias parameter.

----
class C
{
int num;
void foo(alias sym)() { num = sym * 2; }
}

void main()
{
auto c = new C();
int var = 10;
c.foo!var(); // NG, foo!var requires two contexts, 'this' and 'main()'
}
----
)

$(P But, if one context is indirectly accessible from other context, it is allowed.

----
int sum(alias x, alias y)() { return x + y; }

void main()
{
int a = 10;
void nested()
{
int b = 20;
assert(sum!(a, b)() == 30);
}
nested();
}
----

Two local variables $(D a) and $(D b) are in different contexts, but
outer context is indirectly accessible from innter context, so nested
template instance $(D sum!$(LPAREN)a, b$(RPAREN)) will capture only
inner context.
)

$(H2 Recursive Templates)

$(P Template features can be combined to produce some interesting
Expand Down