Thread overview
Distinguishing between const and non-const variable in a template?
Feb 21, 2007
renoX
Feb 22, 2007
mario pernici
Feb 22, 2007
renoX
Feb 22, 2007
Deewiant
Feb 22, 2007
mario pernici
Feb 22, 2007
renoX
Feb 23, 2007
Tyler Knott
Feb 23, 2007
renoX
February 21, 2007
Hello,

I'm trying to improve format string by allowing the format
" ... %{x} ...", my problem is that when I give a non-const char[] parameter in
mixin(Putf!(foo));

then the template fail..

How can I reliably detect in a template if the parameter is a constant or not?

My goal is:
if the parameter isn't a const char[]: leave its name unchanged so that it's parsed by the writef at runtime, if it is a const char[]: parse it myself with the new syntax.

Regards,
renoX

template FindChar(char[] A, char B) {
    static if (A.length == 0) {
        const int FindChar = -1;
    } else static if (A[0] == B) {
        const int FindChar = 0;
    } else static if (-1 == FindChar!(A[1..$], B)) {
		const int FindChar = -1;
	} else {
		const int FindChar = 1 + FindChar!(A[1..$], B);
	}
}

template FmtString(char[] F, A...)
{
	static if (F.length == 0)
		static if (A.length)
			const char[] FmtString = "\"," ~ Fmt!(A);
		else
			const char[] FmtString = "\"";
	else static if (F.length == 1)
		static if (A.length)
			const char[] FmtString = F[0] ~ "\"," ~ Fmt!(A);
		else
			const char[] FmtString = F[0] ~ "\"";
	else static if (F[0..2] == "%%")
		const char[] FmtString = "%%" ~ FmtString!(F[2..$], A);
	else static if (F[0..2] == "%{")
	{
		// get the variable name between %{ and }
		static if (FindChar!(F, '}') <= 2)
			static assert(0, "format %{} incorrect in '" ~ F ~ "'");
		const char[] FmtString = "%s\"," ~ F[2..FindChar!(F,'}')] ~ ",\"" ~
			FmtString!(F[1+FindChar!(F,'}')..$], A);
	}
	else
		const char[] FmtString = F[0] ~ FmtString!(F[1..$], A);
}

template Fmt(A...)
{
	//static assert(0, cast(char*)A[0]);
	static if (A.length == 0)
		const char[] Fmt = "";
	else static if (is(typeof(A[0]) : char[]))
		const char[] Fmt = "\"" ~ FmtString!(A[0], A[1..$]);
	else static if (A.length == 1)
		const char[] Fmt = A[0].stringof;
	else
		const char[] Fmt = A[0].stringof ~ "," ~ Fmt!(A[1..$]);	
}

template Putf(A...)
{
	const char[] Putf = "writef(" ~ Fmt!(A) ~ ");";
}
February 22, 2007
renoX Wrote:

> Hello,
> 
> I'm trying to improve format string by allowing the format
> " ... %{x} ...", my problem is that when I give a non-const char[]
> parameter in
> mixin(Putf!(foo));
> 
> then the template fail..
> 
> How can I reliably detect in a template if the parameter is a constant or not?
> 
> My goal is:
> if the parameter isn't a const char[]: leave its name unchanged so that
> it's parsed by the writef at runtime, if it is a const char[]: parse it
> myself with the new syntax.
> 
> Regards,
> renoX
> 
> template FindChar(char[] A, char B) {
>      static if (A.length == 0) {
>          const int FindChar = -1;
>      } else static if (A[0] == B) {
>          const int FindChar = 0;
>      } else static if (-1 == FindChar!(A[1..$], B)) {
> 		const int FindChar = -1;
> 	} else {
> 		const int FindChar = 1 + FindChar!(A[1..$], B);
> 	}
> }
> 
> template FmtString(char[] F, A...)
> {
> 	static if (F.length == 0)
> 		static if (A.length)
> 			const char[] FmtString = "\"," ~ Fmt!(A);
> 		else
> 			const char[] FmtString = "\"";
> 	else static if (F.length == 1)
> 		static if (A.length)
> 			const char[] FmtString = F[0] ~ "\"," ~ Fmt!(A);
> 		else
> 			const char[] FmtString = F[0] ~ "\"";
> 	else static if (F[0..2] == "%%")
> 		const char[] FmtString = "%%" ~ FmtString!(F[2..$], A);
> 	else static if (F[0..2] == "%{")
> 	{
> 		// get the variable name between %{ and }
> 		static if (FindChar!(F, '}') <= 2)
> 			static assert(0, "format %{} incorrect in '" ~ F ~ "'");
> 		const char[] FmtString = "%s\"," ~ F[2..FindChar!(F,'}')] ~ ",\"" ~
> 			FmtString!(F[1+FindChar!(F,'}')..$], A);
> 	}
> 	else
> 		const char[] FmtString = F[0] ~ FmtString!(F[1..$], A);
> }
> 
> template Fmt(A...)
> {
> 	//static assert(0, cast(char*)A[0]);
> 	static if (A.length == 0)
> 		const char[] Fmt = "";
> 	else static if (is(typeof(A[0]) : char[]))
> 		const char[] Fmt = "\"" ~ FmtString!(A[0], A[1..$]);
> 	else static if (A.length == 1)
> 		const char[] Fmt = A[0].stringof;
> 	else
> 		const char[] Fmt = A[0].stringof ~ "," ~ Fmt!(A[1..$]);
> }
> 
> template Putf(A...)
> {
> 	const char[] Putf = "writef(" ~ Fmt!(A) ~ ");";
> }

Maybe you can use something like this

  const char[] s = "ab";
  static if(is(typeof(&s))) {
    writefln(s, " not constant");
  }
  else {
    static if(is(typeof(s))) {
      writefln(s, " constant");
    }
  }

February 22, 2007
mario pernici a écrit :
> renoX Wrote:
> 
>> Hello,
>>
>> I'm trying to improve format string by allowing the format
>> " ... %{x} ...", my problem is that when I give a non-const char[] parameter in
>> mixin(Putf!(foo));
>>
>> then the template fail..
>>
>> How can I reliably detect in a template if the parameter is a constant or not?
[cut]
> 
> Maybe you can use something like this
> 
>   const char[] s = "ab";
>   static if(is(typeof(&s))) {
>     writefln(s, " not constant");
>   }
>   else {
>     static if(is(typeof(s))) {
>       writefln(s, " constant");
>     }
>   }

Very nice, thanks!

I'm curious: how did you find this??
I don't recall seeing it in the documentation and it looks kind of magical to me..

Thanks again,
renoX

February 22, 2007
renoX wrote:
> mario pernici a écrit :
>> renoX Wrote:
>>
>>> Hello,
>>>
>>> I'm trying to improve format string by allowing the format
>>> " ... %{x} ...", my problem is that when I give a non-const char[]
>>> parameter in
>>> mixin(Putf!(foo));
>>>
>>> then the template fail..
>>>
>>> How can I reliably detect in a template if the parameter is a constant or not?
> [cut]
>>
>> Maybe you can use something like this
>>
>>   const char[] s = "ab";
>>   static if(is(typeof(&s))) {
>>     writefln(s, " not constant");
>>   }
>>   else {
>>     static if(is(typeof(s))) {
>>       writefln(s, " constant");
>>     }
>>   }
> 
> Very nice, thanks!
> 
> I'm curious: how did you find this??
> I don't recall seeing it in the documentation and it looks kind of
> magical to me..
> 
> Thanks again,
> renoX
> 

He probably just figured it out: you can't take the address of a constant, so
is(typeof(&s)) is false if s is a constant. I'm not sure about the necessity of
the is(typeof(s)) in the else case, though.

-- 
Remove ".doesnotlike.spam" from the mail address.
February 22, 2007
Deewiant Wrote:

> renoX wrote:
> > mario pernici a écrit :
> >> renoX Wrote:
> >>
> >>> Hello,
> >>>
> >>> I'm trying to improve format string by allowing the format
> >>> " ... %{x} ...", my problem is that when I give a non-const char[]
> >>> parameter in
> >>> mixin(Putf!(foo));
> >>>
> >>> then the template fail..
> >>>
> >>> How can I reliably detect in a template if the parameter is a constant or not?
> > [cut]
> >>
> >> Maybe you can use something like this
> >>
> >>   const char[] s = "ab";
> >>   static if(is(typeof(&s))) {
> >>     writefln(s, " not constant");
> >>   }
> >>   else {
> >>     static if(is(typeof(s))) {
> >>       writefln(s, " constant");
> >>     }
> >>   }
> > 
> > Very nice, thanks!
> > 
> > I'm curious: how did you find this??
> > I don't recall seeing it in the documentation and it looks kind of
> > magical to me..
> > 
> > Thanks again,
> > renoX
> > 
> 
> He probably just figured it out: you can't take the address of a constant, so
> is(typeof(&s)) is false if s is a constant. I'm not sure about the necessity of
> the is(typeof(s)) in the else case, though.
> 

I learned about static if (is(typeof(...)))
from the post by Kirk McDonald
in the recent thread
"Testing if a function is defined in a module".

The else clause static if(is(typeof(s))) is to make
sure that s exists.

February 22, 2007
mario pernici a écrit :
> Deewiant Wrote:
> 
>> renoX wrote:
>>> mario pernici a écrit :
>>>> renoX Wrote:
>>>>
>>>>> Hello,
>>>>>
>>>>> I'm trying to improve format string by allowing the format
>>>>> " ... %{x} ...", my problem is that when I give a non-const char[]
>>>>> parameter in
>>>>> mixin(Putf!(foo));
>>>>>
>>>>> then the template fail..
>>>>>
>>>>> How can I reliably detect in a template if the parameter is a
>>>>> constant or not?
>>> [cut]
>>>> Maybe you can use something like this
>>>>
>>>>   const char[] s = "ab";
>>>>   static if(is(typeof(&s))) {
>>>>     writefln(s, " not constant");
>>>>   }
>>>>   else {
>>>>     static if(is(typeof(s))) {
>>>>       writefln(s, " constant");
>>>>     }
>>>>   }
>>> Very nice, thanks!
>>>
>>> I'm curious: how did you find this??
>>> I don't recall seeing it in the documentation and it looks kind of
>>> magical to me..
>>>
>>> Thanks again,
>>> renoX
>>>
>> He probably just figured it out: you can't take the address of a constant, so
>> is(typeof(&s)) is false if s is a constant. I'm not sure about the necessity of
>> the is(typeof(s)) in the else case, though.
>>
> 
> I learned about static if (is(typeof(...)))
> from the post by Kirk McDonald
> in the recent thread "Testing if a function is defined in a module".
> 
> The else clause static if(is(typeof(s))) is to make
> sure that s exists.

Mmm; how could s doesn't exist?
D is supposed to be a statically typed language..

renoX
February 23, 2007
renoX wrote:
> mario pernici a écrit :
>> Deewiant Wrote:
>>
>>> renoX wrote:
>>>> mario pernici a écrit :
>>>>> renoX Wrote:
>>>>>
>>>>>> Hello,
>>>>>>
>>>>>> I'm trying to improve format string by allowing the format
>>>>>> " ... %{x} ...", my problem is that when I give a non-const char[]
>>>>>> parameter in
>>>>>> mixin(Putf!(foo));
>>>>>>
>>>>>> then the template fail..
>>>>>>
>>>>>> How can I reliably detect in a template if the parameter is a
>>>>>> constant or not?
>>>> [cut]
>>>>> Maybe you can use something like this
>>>>>
>>>>>   const char[] s = "ab";
>>>>>   static if(is(typeof(&s))) {
>>>>>     writefln(s, " not constant");
>>>>>   }
>>>>>   else {
>>>>>     static if(is(typeof(s))) {
>>>>>       writefln(s, " constant");
>>>>>     }
>>>>>   }
>>>> Very nice, thanks!
>>>>
>>>> I'm curious: how did you find this??
>>>> I don't recall seeing it in the documentation and it looks kind of
>>>> magical to me..
>>>>
>>>> Thanks again,
>>>> renoX
>>>>
>>> He probably just figured it out: you can't take the address of a constant, so
>>> is(typeof(&s)) is false if s is a constant. I'm not sure about the necessity of
>>> the is(typeof(s)) in the else case, though.
>>>
>>
>> I learned about static if (is(typeof(...)))
>> from the post by Kirk McDonald
>> in the recent thread "Testing if a function is defined in a module".
>>
>> The else clause static if(is(typeof(s))) is to make
>> sure that s exists.
> 
> Mmm; how could s doesn't exist?
> D is supposed to be a statically typed language..
> 
> renoX

version (Something) import some.convoluted.library;
else                import some.limited.standIn;

// ... much later in module
static if(is(typeof(someSymbolNotInStandIn))) {
  // ... do things usual way
}
else {
  // ... do it a work-around way for the stand in's case
}

-- Chris Nicholson-Sauls
February 23, 2007
Chris Nicholson-Sauls wrote:
> renoX wrote:
>> mario pernici a écrit :
>>> Deewiant Wrote:
>>>
>>>> renoX wrote:
>>>>> mario pernici a écrit :
>>>>>> renoX Wrote:
>>>>>>
>>>>>>> Hello,
>>>>>>>
>>>>>>> I'm trying to improve format string by allowing the format
>>>>>>> " ... %{x} ...", my problem is that when I give a non-const char[]
>>>>>>> parameter in
>>>>>>> mixin(Putf!(foo));
>>>>>>>
>>>>>>> then the template fail..
>>>>>>>
>>>>>>> How can I reliably detect in a template if the parameter is a
>>>>>>> constant or not?
>>>>> [cut]
>>>>>> Maybe you can use something like this
>>>>>>
>>>>>>   const char[] s = "ab";
>>>>>>   static if(is(typeof(&s))) {
>>>>>>     writefln(s, " not constant");
>>>>>>   }
>>>>>>   else {
>>>>>>     static if(is(typeof(s))) {
>>>>>>       writefln(s, " constant");
>>>>>>     }
>>>>>>   }
>>>>> Very nice, thanks!
>>>>>
>>>>> I'm curious: how did you find this??
>>>>> I don't recall seeing it in the documentation and it looks kind of
>>>>> magical to me..
>>>>>
>>>>> Thanks again,
>>>>> renoX
>>>>>
>>>> He probably just figured it out: you can't take the address of a constant, so
>>>> is(typeof(&s)) is false if s is a constant. I'm not sure about the necessity of
>>>> the is(typeof(s)) in the else case, though.
>>>>
>>>
>>> I learned about static if (is(typeof(...)))
>>> from the post by Kirk McDonald
>>> in the recent thread "Testing if a function is defined in a module".
>>>
>>> The else clause static if(is(typeof(s))) is to make
>>> sure that s exists.
>>
>> Mmm; how could s doesn't exist?
>> D is supposed to be a statically typed language..
>>
>> renoX
> 
> version (Something) import some.convoluted.library;
> else                import some.limited.standIn;
> 
> // ... much later in module
> static if(is(typeof(someSymbolNotInStandIn))) {
>   // ... do things usual way
> }
> else {
>   // ... do it a work-around way for the stand in's case
> }
> 
> -- Chris Nicholson-Sauls

Yes its a contrived example, but its possible.  (And yes I know in this case version(Something) would be better than static-if, but what if the stand-in is subject to change without notice?)

Another use case: a hypothetical GUI library that, in the absence of a main routine, uses its own generic one?  (Wouldn't work all GUI lib designs, of course.)

-- Chris Nicholson-Sauls
February 23, 2007
renoX wrote:
> mario pernici a écrit :
>>
>> The else clause static if(is(typeof(s))) is to make
>> sure that s exists.
> 
> Mmm; how could s doesn't exist?
> D is supposed to be a statically typed language..
> 
> renoX

D is statically typed, but that has nothing to do with why the second test is needed.  Here's a quote from the language spec on the typeof(Expression) construct (http://www.digitalmars.com/d/declaration.html):

> Expression is not evaluated, just the type of it is generated:
> void func()
> {   int i = 1;
>     typeof(++i) j;	// j is declared to be an int, i is not incremented
>     printf("%d\n", i);	// prints 1
> }

And then if you look at the documentation for IsExpression, you'll see that invalid types used in the context of an IsExpression won't error, but instead cause the IsExpression to return 0 (false).  When you combine these two properties, it is legal and valid for D code to take the type of an undeclared variable (resulting in an invalid type) inside an IsExpression; e.g.:

void main()
{
    int foo;

    static assert(is(typeof(foo)), "foo is a valid variable."); //Valid, doesn't trip
    static assert(is(typeof(undeclaredVariable)), "undeclaredVariable is invalid."); //Valid, trips
    typeof(foo) bar; //Valid, bar is an int
    typeof(undeclaredVariable) bar; //"Error: undefined identifier undeclaredVariable"
}

This is why two tests are required for Mario Pernici's const dectector.  The first test determines if you can take the address of the variable, which fails for both const and invalid variables.  Then, it needs to make sure the variable actually exists by taking its type (invalid variables will fail here, but everything else will pass).
February 23, 2007
Tyler Knott a écrit :
> renoX wrote:
>> mario pernici a écrit :
>>>
>>> The else clause static if(is(typeof(s))) is to make
>>> sure that s exists.
>>
>> Mmm; how could s doesn't exist?
>> D is supposed to be a statically typed language..
>>
>> renoX
> 
> D is statically typed, but that has nothing to do with why the second test is needed.  Here's a quote from the language spec on the typeof(Expression) construct (http://www.digitalmars.com/d/declaration.html):
> 
>  > Expression is not evaluated, just the type of it is generated:
>  > void func()
>  > {   int i = 1;
>  >     typeof(++i) j;    // j is declared to be an int, i is not incremented
>  >     printf("%d\n", i);    // prints 1
>  > }
> 
> And then if you look at the documentation for IsExpression, you'll see that invalid types used in the context of an IsExpression won't error, but instead cause the IsExpression to return 0 (false).  When you combine these two properties, it is legal and valid for D code to take the type of an undeclared variable (resulting in an invalid type) inside an IsExpression; e.g.:
> 
> void main()
> {
>     int foo;
> 
>     static assert(is(typeof(foo)), "foo is a valid variable."); //Valid, doesn't trip
>     static assert(is(typeof(undeclaredVariable)), "undeclaredVariable is invalid."); //Valid, trips
>     typeof(foo) bar; //Valid, bar is an int
>     typeof(undeclaredVariable) bar; //"Error: undefined identifier undeclaredVariable"
> }
> 
> This is why two tests are required for Mario Pernici's const dectector.  The first test determines if you can take the address of the variable, which fails for both const and invalid variables.  Then, it needs to make sure the variable actually exists by taking its type (invalid variables will fail here, but everything else will pass).

Very clear explanation, thanks!
renoX