digitalmars.D.bugs - [Issue 9088] New: static static
- d-bugmail puremagic.com (160/160) Nov 27 2012 http://d.puremagic.com/issues/show_bug.cgi?id=9088
- d-bugmail puremagic.com (53/53) Nov 29 2012 http://d.puremagic.com/issues/show_bug.cgi?id=9088
- d-bugmail puremagic.com (36/44) Nov 29 2012 http://d.puremagic.com/issues/show_bug.cgi?id=9088
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 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: -------
Nov 27 2012
http://d.puremagic.com/issues/show_bug.cgi?id=9088 Andrej Mitrovic <andrej.mitrovich gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |andrej.mitrovich gmail.com 05:15:58 PST --- For functions: I think this might be the job of the compiler/linker. If there are multiple static function definitions that are identical they should be merged, there's no need for the user to do the job of the compiler/linker. As for template-shared variables, they are possible without bloating the global (module) scope by using templates, for example: template Wrap() { int globX; struct Foo(T) { alias globX x; } } alias Wrap!().Foo Foo; void main() { Foo!(int) f1; Foo!(float) f2; f1.x = 10; assert(f2.x == 10); } You could probably implement some kind of helper mixin that could implement this automatically. Here's the same trick with function templates: import std.stdio; template Wrap() { dchar[] table = ['a', 'b', 'c']; immutable val = 10; int foo(T)() if (is(T == char) || is(T == dchar) || is(T == wchar)) { writeln(&table); return 0; } } alias Wrap!().foo foo; void main() { alias foo!char fooChar; alias foo!dchar fooDChar; // both print the same address fooChar(); fooDChar(); } -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Nov 29 2012
http://d.puremagic.com/issues/show_bug.cgi?id=9088 Thank you for the answers Andrej.For functions: I think this might be the job of the compiler/linker. If there are multiple static function definitions that are identical they should be merged, there's no need for the user to do the job of the compiler/linker.Notes: - Currently DMD is not doing this, and I think it's not going to do it soon (I don't know about GDC2, possibly GCC is able to do this, but see below); - I think LDC2 is able to do this, but you have to add a compilation switch that is not included in "-O4" switch and that no one uses or knows about (something avout merging functions); - I think in C if you have many different functions that contain the same code, and you take their address, the C language requires them to be different. I assume D shares the same C rule. So LDC is forced to leave stubs that contain a jump, it can't delete them fully; - Avoiding to create bloat in the first place is faster for the compiler+linker than compiling them and then not linking them (or recognizing them as equal and replacing them with a jump to just one implementation). I agree that in theory the compiler can avoid creating those copies in the first place, because it knows they come from a template, but I think currently LDC2 is not this smart; - A "static static" annotation is a contract between compiler and programmer, like "pure". This means that if you try to use the template arguments (that are types and compile-time values) in a "static static" template struct method, the compiler complaints at compile-time (something similar happens if you try to use a locally defined variable in a static nested function). This enforcing can't happen if such feature is left to compiler/linker optimizations. - Generally I think D needs somethiong to help fight template bloat. "static static" is not very good for this, but I think it helps.As for template-shared variables, they are possible without bloating the global (module) scope by using templates, for example: ... You could probably implement some kind of helper mixin that could implement this automatically."static static" is not essential, because there's always the possibility to move things to module scope. On the other hand if you take a look at topswops(), a "static static" annotation allows to minimize the amount of changes in the code when you want to turn a function into a template (and avoid moving static variabiles to globals). So it's not essential, but I think it's handy, in some cases. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Nov 29 2012