| |
 | Posted by bearophile_hugs@eml.cc | Permalink Reply |
|
bearophile_hugs@eml.cc 
| http://d.puremagic.com/issues/show_bug.cgi?id=9088
Summary: static static
Product: D
Version: D2
Platform: All
OS/Version: All
Status: NEW
Severity: enhancement
Priority: P2
Component: DMD
AssignedTo: nobody@puremagic.com
ReportedBy: bearophile_hugs@eml.cc
--- Comment #0 from bearophile_hugs@eml.cc 2012-11-27 19:20:05 PST ---
Here I suggest an enhancement for D language (that is pure an addition, it should cause no breaking changes).
The introduction of "static static", it roughly means "static regarding to the template". This means that if a template contains a "static static" item, it is shared among all instantiations of the template.
Here are some use cases:
- - - - - - - - - - - - - -
Templated classes or templated structs sometimes have member functions that are meant to be shared among all the instantiations of the template.
struct Foo(T) {
T x;
static static int sqr(int x) { return x ^^ 2; }
}
Here static static allows to reduce template bloat, because only one sqr()
function is generated regardless the T type. Putting sqr() outside Foo is an
alternative solution, but it loses the packaging given by Foo itself (despite
the usefulness of universal call syntax).
A third solution is to put inside Foo() just a stub function that calls an
external private _sqr() function. This has both the advantage of keeping sqr()
in the the namespace, and keeps the template bloat low because only very little
sqr() are generated for each instantiation of Foo. But this requires parameter
fowarding and increases code complexity a little.
- - - - - - - - - - - - - -
There are cases where templated functions need a static variable that is shared among all instantiations of the template:
int foo(T)() if (is(T == char) || is(T == dchar) || is(T == wchar)) {
static static dchar[] table = ['a', 'b', 'c'];
static static immutable val = someHeavyCTFE(10);
// uses table and val here
return 0;
}
In this case static static is useful to save memory, because only one table is present for all instantiations of Foo. And only one val is computed, saving compilation time.
An alternative design is to put both table and val outside foo(), but this adds more global names that are better kept inside foo(), just like a static variable in a function avoids names at module level.
This example program computes some numbers:
uint topswops(size_t nMax = 16)(in size_t n) nothrow
in {
assert(n > 0 && n < nMax);
} body {
size_t d = 0;
__gshared uint[nMax] best;
alias T = byte;
alias Deck = T[nMax];
void trySwaps(in ref Deck deck, in uint f) nothrow {
if (d > best[n])
best[n] = d;
foreach_reverse (immutable i; 0 .. n) {
if (deck[i] == -1 || deck[i] == i)
break;
if (d + best[i] <= best[n])
return;
}
Deck deck2 = deck;
d++;
uint k = 1;
foreach (immutable i; 1 .. n) {
k <<= 1;
if (deck2[i] == -1) {
if (f & k)
continue;
} else if (deck2[i] != i)
continue;
deck2[0] = cast(T)i;
foreach_reverse (immutable j; 0 .. i)
deck2[i - j] = deck[j];
trySwaps(deck2, f | k);
}
d--;
}
best[n] = 0;
Deck deck0 = -1;
deck0[0] = 0;
trySwaps(deck0, 1);
return best[n];
}
import std.stdio;
void main() {
foreach (i; 1 .. 13)
writefln("%2d: %d", i, topswops(i));
}
To speed up the computation the n argument is now a compile-time value, and
topswops() is a template:
import std.stdio, std.typetuple;
template Range(int start, int stop) {
static if (stop <= start)
alias TypeTuple!() Range;
else
alias TypeTuple!(Range!(start, stop - 1), stop - 1) Range;
}
__gshared uint[32] best;
uint topswops(size_t n)() nothrow {
static assert(n > 0 && n < best.length);
size_t d = 0;
alias T = byte;
alias Deck = T[n];
void trySwaps(in ref Deck deck, in uint f) nothrow {
if (d > best[n])
best[n] = d;
foreach_reverse (immutable i; Range!(0, n)) {
if (deck[i] == -1 || deck[i] == i)
break;
if (d + best[i] <= best[n])
return;
}
Deck deck2 = void;
foreach (immutable i; Range!(0, n)) // Copy.
deck2[i] = deck[i];
d++;
foreach (immutable i; Range!(1, n)) {
enum uint k = 1U << i;
if (deck2[i] == -1) {
if (f & k)
continue;
} else if (deck2[i] != i)
continue;
deck2[0] = cast(T)i;
foreach_reverse (immutable j; Range!(0, i))
deck2[i - j] = deck[j]; // Reverse copy.
trySwaps(deck2, f | k);
}
d--;
}
best[n] = 0;
Deck deck0 = -1;
deck0[0] = 0;
trySwaps(deck0, 1);
return best[n];
}
void main() {
foreach (i; Range!(1, 13))
writefln("%2d: %d", i, topswops!i());
}
To work correctly this program requires a static variable "best". Once topswops() becomes a template, "best" must be moved to module scope. This is avoided by static static:
...
uint topswops(size_t n)() nothrow {
static assert(n > 0 && n < best.length);
size_t d = 0;
static static __gshared uint[32] best;
...
--
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
|