Jump to page: 1 2
Thread overview
compile-time variables?
May 22, 2007
BCS
May 22, 2007
Pragma
May 22, 2007
Pragma
May 22, 2007
Fraser
May 23, 2007
Bill Baxter
May 23, 2007
Pragma
May 23, 2007
Bill Baxter
May 23, 2007
Pragma
May 24, 2007
Fraser
May 26, 2007
Bruno Medeiros
May 26, 2007
David B. Held
May 28, 2007
BCS
May 28, 2007
David B. Held
May 29, 2007
BCS
May 30, 2007
David B. Held
May 30, 2007
BCS
May 31, 2007
David B. Held
May 30, 2007
Don Clugston
May 22, 2007
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
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
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
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
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
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
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
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
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
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

« First   ‹ Prev
1 2