| Thread overview | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
May 22, 2007 compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Hi, D has some pretty impressive metaprogramming features, but I was wondering if there was any way to do compile-time variables, or fake it with recursive templates or something. In particular, I want a mixin template that mixes in a unique numeric ID as part of a function every time it's used in a mixin. Thouands of these functions will be used in the program, and each must have a unique ID in an associative array (after they're called for the first time, lazy eval is being done at runtime). If I can avoid code bloat, all the Better! Thanks, Fraser | ||||
May 22, 2007 Re: compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Fraser, Robert Fraser | Reply to Robert,
> Hi,
>
> D has some pretty impressive metaprogramming features, but I was
> wondering if there was any way to do compile-time variables, or fake
> it with recursive templates or something. In particular, I want a
> mixin template that mixes in a unique numeric ID as part of a
> function every time it's used in a mixin. Thouands of these functions
> will be used in the program, and each must have a unique ID in an
> associative array (after they're called for the first time, lazy eval
> is being done at runtime).
>
> If I can avoid code bloat, all the Better!
>
> Thanks,
> Fraser
it's does stuff at runtime but this should work
int val = 0
class UID(char[] file, int line, A...)
{
static const uid;
static this(){uid = val++}
}
UID!(__FILE__, __LINE__).uid
if it is used in a template, add enough of the template args to make each instance have different args.
| |||
May 22, 2007 Re: compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Fraser, Robert Fraser | Fraser wrote: > In particular, I want a mixin template that mixes in a unique numeric ID as part of a function every time it's used in a mixin. This is possible, but it will take some mildly ugly code to accomplish*. The major hurdle to overcome is that templates are effectively stateless. This is because every template invocation is dependent upon its arguments and global constants for evaluation, and is unable to change anything but its own definition. The result is that there's no compile-time analog for simple stuff like this: uint value; void Counter(){ value++; } ... since 'Counter' modifies 'value', which would be a stateful evaluation. The solution is to have the template evaluate to a counter value that is used as an argument for subsequent calls to the template. // un-tested template Counter(){ const uint Counter = 1; } template Counter(lastCounter){ const uint Counter= lastCounter + 1; } tempalte MyTemplate(){ alias Counter!() first; alias Counter!(first) second; alias Counter!(second) last; alias last next; // use MyTemplate!().next where needed to keep counting } The gist is to keep passing around your 'counter' as you would an object at runtime. It's kind of ugly this way, but it should clean up a little if you use CTFE: // un-tested uint Counter(){ return 1; } uint Counter(in uint value){ return value+1; } uint MyFn(){ const auto first = Counter(); const auto second = Counter(first); const auto third = Counter(second); return third; } (* welcome to meta-programming) -- - EricAnderton at yahoo | |||
May 22, 2007 Re: compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to BCS | BCS wrote: > Reply to Robert, > >> Hi, >> >> D has some pretty impressive metaprogramming features, but I was >> wondering if there was any way to do compile-time variables, or fake >> it with recursive templates or something. In particular, I want a >> mixin template that mixes in a unique numeric ID as part of a >> function every time it's used in a mixin. Thouands of these functions >> will be used in the program, and each must have a unique ID in an >> associative array (after they're called for the first time, lazy eval >> is being done at runtime). >> >> If I can avoid code bloat, all the Better! >> >> Thanks, >> Fraser > > it's does stuff at runtime but this should work > > int val = 0 > > class UID(char[] file, int line, A...) > { > static const uid; > static this(){uid = val++} > } > > UID!(__FILE__, __LINE__).uid > > if it is used in a template, add enough of the template args to make each instance have different args. > > BCS, I like that hack. Technically, isn't this a runtime solution since the evaluation of 'val++' is done at runtime? Then again, if all Robert wants is a registry of template instances or somesuch (regardless of performance), this might do the trick. -- - EricAnderton at yahoo | |||
May 22, 2007 Re: compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Pragma | Thanks for the ideas! Unfortunately, it doesn't seem to be working (either way). Here's the complete code I tried:
--------------------
import tango.io.Stdout;
char[] ctfe_itoa(uint value)
{
if (value < 10) return "0123456789"[value .. value+1];
return ctfe_itoa(value / 10) ~ ctfe_itoa(value % 10);
}
uint Counter(){
return 1;
}
uint Counter(in uint value){
return value+1;
}
uint nextID()
{
const auto first = Counter();
const auto second = Counter(first);
const auto third = Counter(second);
return third;
}
template Foo(char[] name)
{
const char[] text = "const char[] " ~ name ~ " = \"Name: " ~ name ~ ", ID: " ~ ctfe_itoa(nextID()) ~ "\n\";";
}
mixin(Foo!("a").text);
mixin(Foo!("b").text);
mixin(Foo!("c").text);
mixin(Foo!("d").text);
mixin(Foo!("e").text);
mixin(Foo!("f").text);
int main(char[][] args)
{
Stdout(a)(b)(c)(d)(e)(f);
return 0;
}
--------------------
The result was:
Name: a, ID: 3
Name: b, ID: 3
Name: c, ID: 3
Name: d, ID: 3
Name: e, ID: 3
Name: f, ID: 3
A similar thing happened with the template example.
Pragma Wrote:
> Fraser wrote:
> > <snip>
| |||
May 23, 2007 Re: compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Fraser | Fraser wrote:
> Thanks for the ideas! Unfortunately, it doesn't seem to be working (either way). Here's the complete code I tried:
>
> --------------------
> import tango.io.Stdout;
>
> char[] ctfe_itoa(uint value)
> {
> if (value < 10) return "0123456789"[value .. value+1];
> return ctfe_itoa(value / 10) ~ ctfe_itoa(value % 10);
> }
>
> uint Counter(){
> return 1;
> }
>
> uint Counter(in uint value){
> return value+1;
> }
>
> uint nextID()
> {
> const auto first = Counter();
> const auto second = Counter(first);
> const auto third = Counter(second);
> return third;
> }
Looks to me like this is doing exactly what you asked it to: return 3 no matter what.
I think you need a static variable like
uint nextID() {
static uint id=0;
return ++id;
}
Unless I'm missing something.
--bb
| |||
May 23, 2007 Re: compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Fraser | Fraser wrote: > Thanks for the ideas! Unfortunately, it doesn't seem to be working (either way). Here's the complete code I tried: > > -------------------- > import tango.io.Stdout; > > char[] ctfe_itoa(uint value) > { > if (value < 10) return "0123456789"[value .. value+1]; > return ctfe_itoa(value / 10) ~ ctfe_itoa(value % 10); > } > > uint Counter(){ > return 1; > } > > uint Counter(in uint value){ > return value+1; > } > > uint nextID() > { > const auto first = Counter(); > const auto second = Counter(first); > const auto third = Counter(second); > return third; > } > > template Foo(char[] name) > { const char[] text = "const char[] " ~ name ~ " = \"Name: " ~ name ~ ", ID: " ~ ctfe_itoa(nextID()) ~ "\n\";"; > } > > mixin(Foo!("a").text); > mixin(Foo!("b").text); > mixin(Foo!("c").text); > mixin(Foo!("d").text); > mixin(Foo!("e").text); > mixin(Foo!("f").text); > > int main(char[][] args) > { > Stdout(a)(b)(c)(d)(e)(f); > return 0; > } > -------------------- > > The result was: > Name: a, ID: 3 > Name: b, ID: 3 > Name: c, ID: 3 > Name: d, ID: 3 > Name: e, ID: 3 > Name: f, ID: 3 > > A similar thing happened with the template example. You need to prime your sequence with the zero counter value, and then keep passing a template instance around to continue to count up: template StartCounter(){ const uint next = 0; } template Foo(char[] name,alias counter) { const char[] text = "const char[] " ~ name ~ " = \"Name: " ~ name ~ ", ID: " ~ ctfe_itoa(counter.next) ~ "\n\";"; const uint next = counter.next + 1; } alias Foo!("a",StartCounter!()) foo_a; alias Foo!("b",foo_a) foo_b; alias Foo!("c",foo_b) foo_c; alias Foo!("d",foo_c) foo_d; // <-- note how we feed the previous template back into the next template instance alias Foo!("e",foo_d) foo_e; mixin(foo_a.text); mixin(foo_b.text); mixin(foo_c.text); mixin(foo_d.text); mixin(foo_e.text); -- - EricAnderton at yahoo | |||
May 23, 2007 Re: compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Pragma | Pragma wrote:
> Fraser wrote:
>> Thanks for the ideas! Unfortunately, it doesn't seem to be working (either way). Here's the complete code I tried:
>>
>> --------------------
>> import tango.io.Stdout;
>>
>> char[] ctfe_itoa(uint value)
>> {
>> if (value < 10) return "0123456789"[value .. value+1];
>> return ctfe_itoa(value / 10) ~ ctfe_itoa(value % 10);
>> }
>>
>> uint Counter(){
>> return 1;
>> }
>>
>> uint Counter(in uint value){
>> return value+1;
>> }
>>
>> uint nextID()
>> {
>> const auto first = Counter();
>> const auto second = Counter(first);
>> const auto third = Counter(second);
>> return third;
>> }
>>
>> template Foo(char[] name)
>> { const char[] text = "const char[] " ~ name ~ " = \"Name: " ~ name ~ ", ID: " ~ ctfe_itoa(nextID()) ~ "\n\";";
>> }
>>
>> mixin(Foo!("a").text);
>> mixin(Foo!("b").text);
>> mixin(Foo!("c").text);
>> mixin(Foo!("d").text);
>> mixin(Foo!("e").text);
>> mixin(Foo!("f").text);
>>
>> int main(char[][] args)
>> {
>> Stdout(a)(b)(c)(d)(e)(f);
>> return 0;
>> }
>> --------------------
>>
>> The result was:
>> Name: a, ID: 3
>> Name: b, ID: 3
>> Name: c, ID: 3
>> Name: d, ID: 3
>> Name: e, ID: 3
>> Name: f, ID: 3
>>
>> A similar thing happened with the template example.
>
> You need to prime your sequence with the zero counter value, and then keep passing a template instance around to continue to count up:
>
> template StartCounter(){
> const uint next = 0;
> }
>
> template Foo(char[] name,alias counter)
> {
> const char[] text = "const char[] " ~ name ~ " = \"Name: " ~ name ~ ", ID: " ~ ctfe_itoa(counter.next) ~ "\n\";";
> const uint next = counter.next + 1;
> }
>
> alias Foo!("a",StartCounter!()) foo_a;
> alias Foo!("b",foo_a) foo_b;
> alias Foo!("c",foo_b) foo_c;
> alias Foo!("d",foo_c) foo_d; // <-- note how we feed the previous template back into the next template instance
> alias Foo!("e",foo_d) foo_e;
>
> mixin(foo_a.text);
> mixin(foo_b.text);
> mixin(foo_c.text);
> mixin(foo_d.text);
> mixin(foo_e.text);
>
The problem with that (I'm assuming) is that if he could arrange it so that the generated code contained a nice ordered sequence of template names in increasing order, then that means he could also just as easily generate IDs themselves without going through all the template hoops.
--bb
| |||
May 23, 2007 Re: compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bill Baxter | Bill Baxter wrote: > Pragma wrote: >> Fraser wrote: >>> Thanks for the ideas! Unfortunately, it doesn't seem to be working (either way). Here's the complete code I tried: >>> >>> -------------------- >>> import tango.io.Stdout; >>> >>> char[] ctfe_itoa(uint value) >>> { >>> if (value < 10) return "0123456789"[value .. value+1]; >>> return ctfe_itoa(value / 10) ~ ctfe_itoa(value % 10); >>> } >>> >>> uint Counter(){ >>> return 1; >>> } >>> >>> uint Counter(in uint value){ >>> return value+1; >>> } >>> >>> uint nextID() >>> { >>> const auto first = Counter(); >>> const auto second = Counter(first); >>> const auto third = Counter(second); >>> return third; >>> } >>> >>> template Foo(char[] name) >>> { const char[] text = "const char[] " ~ name ~ " = \"Name: " ~ name ~ ", ID: " ~ ctfe_itoa(nextID()) ~ "\n\";"; >>> } >>> >>> mixin(Foo!("a").text); >>> mixin(Foo!("b").text); >>> mixin(Foo!("c").text); >>> mixin(Foo!("d").text); >>> mixin(Foo!("e").text); >>> mixin(Foo!("f").text); >>> >>> int main(char[][] args) >>> { >>> Stdout(a)(b)(c)(d)(e)(f); >>> return 0; >>> } >>> -------------------- >>> >>> The result was: >>> Name: a, ID: 3 >>> Name: b, ID: 3 >>> Name: c, ID: 3 >>> Name: d, ID: 3 >>> Name: e, ID: 3 >>> Name: f, ID: 3 >>> >>> A similar thing happened with the template example. >> >> You need to prime your sequence with the zero counter value, and then keep passing a template instance around to continue to count up: >> >> template StartCounter(){ >> const uint next = 0; >> } >> >> template Foo(char[] name,alias counter) >> { >> const char[] text = "const char[] " ~ name ~ " = \"Name: " ~ name ~ ", ID: " ~ ctfe_itoa(counter.next) ~ "\n\";"; >> const uint next = counter.next + 1; >> } >> >> alias Foo!("a",StartCounter!()) foo_a; >> alias Foo!("b",foo_a) foo_b; >> alias Foo!("c",foo_b) foo_c; >> alias Foo!("d",foo_c) foo_d; // <-- note how we feed the previous template back into the next template instance >> alias Foo!("e",foo_d) foo_e; >> >> mixin(foo_a.text); >> mixin(foo_b.text); >> mixin(foo_c.text); >> mixin(foo_d.text); >> mixin(foo_e.text); >> > > The problem with that (I'm assuming) is that if he could arrange it so that the generated code contained a nice ordered sequence of template names in increasing order, then that means he could also just as easily generate IDs themselves without going through all the template hoops. > > --bb Possibly. It all depends on the complexity of his solution - the trivial example above doesn't do it much justice. Where this really comes in handy is when you need to repeat any such block of declarations: template Foo!(alias counter){ alias Foo!("a",StartCounter!()) foo_a; alias Foo!("b",foo_a) foo_b; alias Foo!("c",foo_b) foo_c; mixin(foo_a.text); mixin(foo_b.text); mixin(foo_c.text); alias foo_c.next next; // save the counter state for the next use } alias Foo!(StartCounter!()) foo1; alias Foo!(foo1) foo2; alias Foo!(foo2) foo3; -- - EricAnderton at yahoo | |||
May 24, 2007 Re: compile-time variables? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Pragma | Thanks. Yeah, that seems to work, but Bill is right - generating the IDs is probably simpler than trying to ensure I keep a reference to the template each time. Since it might need to go between multiple files, I think I'm probably going to have to look for a runtime solution, anyway. Thanks for your help.
Pragma Wrote:
> Bill Baxter wrote:
> > Pragma wrote:
> >> Fraser wrote:
> >>> Thanks for the ideas! Unfortunately, it doesn't seem to be working (either way). Here's the complete code I tried:
> >>>
> >>> --------------------
> >>> import tango.io.Stdout;
> >>>
> >>> char[] ctfe_itoa(uint value)
> >>> {
> >>> if (value < 10) return "0123456789"[value .. value+1];
> >>> return ctfe_itoa(value / 10) ~ ctfe_itoa(value % 10);
> >>> }
> >>>
> >>> uint Counter(){
> >>> return 1;
> >>> }
> >>>
> >>> uint Counter(in uint value){
> >>> return value+1;
> >>> }
> >>>
> >>> uint nextID()
> >>> {
> >>> const auto first = Counter();
> >>> const auto second = Counter(first);
> >>> const auto third = Counter(second);
> >>> return third;
> >>> }
> >>>
> >>> template Foo(char[] name)
> >>> { const char[] text = "const char[] " ~ name ~ " = \"Name: " ~
> >>> name ~ ", ID: " ~ ctfe_itoa(nextID()) ~ "\n\";";
> >>> }
> >>>
> >>> mixin(Foo!("a").text);
> >>> mixin(Foo!("b").text);
> >>> mixin(Foo!("c").text);
> >>> mixin(Foo!("d").text);
> >>> mixin(Foo!("e").text);
> >>> mixin(Foo!("f").text);
> >>>
> >>> int main(char[][] args)
> >>> {
> >>> Stdout(a)(b)(c)(d)(e)(f);
> >>> return 0;
> >>> }
> >>> --------------------
> >>>
> >>> The result was:
> >>> Name: a, ID: 3
> >>> Name: b, ID: 3
> >>> Name: c, ID: 3
> >>> Name: d, ID: 3
> >>> Name: e, ID: 3
> >>> Name: f, ID: 3
> >>>
> >>> A similar thing happened with the template example.
> >>
> >> You need to prime your sequence with the zero counter value, and then keep passing a template instance around to continue to count up:
> >>
> >> template StartCounter(){
> >> const uint next = 0;
> >> }
> >>
> >> template Foo(char[] name,alias counter)
> >> {
> >> const char[] text = "const char[] " ~ name ~ " = \"Name: " ~ name
> >> ~ ", ID: " ~ ctfe_itoa(counter.next) ~ "\n\";";
> >> const uint next = counter.next + 1;
> >> }
> >>
> >> alias Foo!("a",StartCounter!()) foo_a;
> >> alias Foo!("b",foo_a) foo_b;
> >> alias Foo!("c",foo_b) foo_c;
> >> alias Foo!("d",foo_c) foo_d; // <-- note how we feed the previous
> >> template back into the next template instance
> >> alias Foo!("e",foo_d) foo_e;
> >>
> >> mixin(foo_a.text);
> >> mixin(foo_b.text);
> >> mixin(foo_c.text);
> >> mixin(foo_d.text);
> >> mixin(foo_e.text);
> >>
> >
> > The problem with that (I'm assuming) is that if he could arrange it so that the generated code contained a nice ordered sequence of template names in increasing order, then that means he could also just as easily generate IDs themselves without going through all the template hoops.
> >
> > --bb
>
> Possibly. It all depends on the complexity of his solution - the trivial example above doesn't do it much justice. Where this really comes in handy is when you need to repeat any such block of declarations:
>
> template Foo!(alias counter){
> alias Foo!("a",StartCounter!()) foo_a;
> alias Foo!("b",foo_a) foo_b;
> alias Foo!("c",foo_b) foo_c;
>
> mixin(foo_a.text);
> mixin(foo_b.text);
> mixin(foo_c.text);
>
> alias foo_c.next next; // save the counter state for the next use
> }
>
> alias Foo!(StartCounter!()) foo1;
> alias Foo!(foo1) foo2;
> alias Foo!(foo2) foo3;
>
> --
> - EricAnderton at yahoo
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply