www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Distinguishing between const and non-const variable in a template?

reply renoX <renosky free.fr> writes:
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) ~ ");";
}
Feb 21 2007
parent reply mario pernici <mario.pernici mi.infn.it> writes:
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"); } }
Feb 22 2007
parent reply renoX <renosky free.fr> writes:
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?


 
 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
Feb 22 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
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?


 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.
Feb 22 2007
parent reply mario pernici <mario.pernici mi.infn.it> writes:
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?


 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.
Feb 22 2007
parent reply renoX <renosky free.fr> writes:
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?


 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");
     }
   }

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

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
Feb 22 2007
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
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?


 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");
     }
   }

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

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
Feb 22 2007
parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
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?


 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");
     }
   }

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

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
Feb 22 2007
prev sibling parent reply Tyler Knott <tywebmail mailcity.com> writes:
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).
Feb 22 2007
parent renoX <renosky free.fr> writes:
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
Feb 22 2007