www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - The power of static if! (A bit of fun)

reply Don Clugston <dac nospam.com.au> writes:
I really think the docs for "static if" don't do it justice. Since I'm 
new to D templates, I've been trying to come up with a more interesting 
example than the one in the docs. Here's my first attempt. This program 
prints a nicely-formatted list of the first 30 Fibonnacci numbers _at 
compile time_.
Note that except for the clunky 'inttostr' template (which is a general 
utility template, could be quite useful in other contexts), it's not 
much longer than a straightforward implementation in a functional 
programming language.

Enjoy!

-----------------------------
template inttostr(int n)
{
    template digittostr(int n)
    {
	  static if (n==0) const char [] s="0";
	  else static if (n==1) const char [] s="1";
	  else static if (n==2) const char [] s="2";
	  else static if (n==3) const char [] s="3";
	  else static if (n==4) const char [] s="4";
	  else static if (n==5) const char [] s="5";
	  else static if (n==6) const char [] s="6";
	  else static if (n==7) const char [] s="7";
	  else static if (n==8) const char [] s="8";
	  else const char [] s="9";
    }
    static if (n<0) const char [] s = "-" ~ inttostr!(-n).s;
    else static if (n<10) const char [] s = digittostr!(n).s;
    else const char [] s = inttostr!(n/10).s ~ digittostr!(n%10).s;
}

template ordinal(int n)
{
   static if (n==1) const char [] ordinal = inttostr!(n).s ~"st";
   else static if (n==2) const char [] ordinal = inttostr!(n).s ~"nd";
   else static if (n==3) const char [] ordinal = inttostr!(n).s ~"rd";
   else const char [] ordinal = inttostr!(n).s ~"th";
}

template fib(int n)
{
     static if (n==0)      const int f=0;
     else static if (n==1) const int f=1;
     else                  const int f = fib!(n-1).f + fib!(n-2).f;
     static if (n>0) {
        pragma(msg, "The " ~ ordinal!(n).ordinal ~
           " Fibonnacci number is "~ inttostr!(fib!(n-1).f).s);
     }
}

int main()
{
    return fib!(30).f;
    ButItWontActuallyCompile;
}
Oct 25 2005
next sibling parent Don Clugston <dac nospam.com.au> writes:
Here's an improved version, which generates all Fibonnacci numbers which 
are small enough to fit in the given integral type. Try changing the 
last line to short, uint, ulong, etc.
This is something that would be *extremely* hard to do in C++.
I just wish I could improve the digittostr function.

Tested on DMD-Windows 0.136 and 0.137.
-------------------
// Works for any integral type.
template inttostr(Type, Type n)
{
    template digittostr(int n)
    {
       static if (n==0) const char [] s="0";
       else static if (n==1) const char [] s="1";
       else static if (n==2) const char [] s="2";
       else static if (n==3) const char [] s="3";
       else static if (n==4) const char [] s="4";
       else static if (n==5) const char [] s="5";
       else static if (n==6) const char [] s="6";
       else static if (n==7) const char [] s="7";
       else static if (n==8) const char [] s="8";
       else const char [] s="9";
    }
    static if (Type.min<0 && n<0) const char [] s = "-" ~ inttostr!(-n).s;
    else static if (n<10) const char [] s = digittostr!(n).s;
    else const char [] s = inttostr!(Type, n/10).s ~ digittostr!(n%10).s;
}

template ordinal(int n)
{
   static if (n%10==1 && n!=11) const char [] ordinal = inttostr!(int, 
n).s ~"st";
   else static if (n%10==2 && n!=12) const char [] ordinal = 
inttostr!(int, n).s ~"nd";
   else static if (n%10==3 && n!=13) const char [] ordinal = 
inttostr!(int, n).s ~"rd";
   else const char [] ordinal = inttostr!(int, n).s ~"th";
}

// Calculate the nth Fibonnacci number
template fib(Type, int n)
{
     static if (n==1)      const Type f=0;
     else static if (n==2) const Type f=1;
     else                  const Type f = fib!(Type, n-1).f + fib!(Type, 
n-2).f;
}

template showallfibs(Type)
{
	template showfibs(Type, int n)
	{
	   pragma(msg, "The " ~ ordinal!(n).ordinal ~
		  " Fibonnacci number is "~ inttostr!(Type, fib!(Type, n).f).s);
	   static if (fib!(Type, n).f < Type.max/2) {
		   const int show = showfibs!(Type,n+1).show;
	   } else {
		 const int show = 0;
		 pragma(msg, "The " ~ ordinal!(n+1).ordinal ~
		  " Fibonnacci number is greater than " ~ inttostr!(Type, Type.max).s);
	   }
	}
	const int notfinished = showfibs!(Type, 1).show;
}

static assert(showallfibs!(long).notfinished);
Oct 26 2005
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <djn8dp$93g$1 digitaldaemon.com>, Don Clugston says...
I really think the docs for "static if" don't do it justice. Since I'm 
new to D templates, I've been trying to come up with a more interesting 
example than the one in the docs. Here's my first attempt. This program 
prints a nicely-formatted list of the first 30 Fibonnacci numbers _at 
compile time_.

Very cool. I hadn't thought to use pragma(msg) in that way :-) Nor had I realized that the concatenate operator worked on const declarations. This is probably the most template-oriented thing I've done in D so far: http://svn.dsource.org/projects/ares/trunk/src/ares/std/atomic.d Not as fancy, but it's far more compact and readable than it would have been in C++ (note that the unsigned versions don't work because of a compiler bug). Sean
Oct 26 2005
parent reply Don Clugston <dac nospam.com.au> writes:
Sean Kelly wrote:
 In article <djn8dp$93g$1 digitaldaemon.com>, Don Clugston says...
 
I really think the docs for "static if" don't do it justice. Since I'm 
new to D templates, I've been trying to come up with a more interesting 
example than the one in the docs. Here's my first attempt. This program 
prints a nicely-formatted list of the first 30 Fibonnacci numbers _at 
compile time_.

Very cool. I hadn't thought to use pragma(msg) in that way :-) Nor had I realized that the concatenate operator worked on const declarations.

It was pure speculation on my part. I did not expect either of those things to work. I haven't yet worked out what a 'literal' is in D, but it is much more generous than C++. For example, casts are permitted. This is
 probably the most template-oriented thing I've done in D so far:
 
 http://svn.dsource.org/projects/ares/trunk/src/ares/std/atomic.d
 
 Not as fancy, but it's far more compact and readable than it would have been in
 C++ (note that the unsigned versions don't work because of a compiler bug).

Nice. I haven't used the 'is' operator yet, I think it has enormous potential too. And now we have 'auto' typing... I've also made a template that calculates pi at compile time by summing a power series. I have hopes that I can make it work to arbitrary precision (it only does reals right now). Seriously, this compiler is *far* more advanced than the programming techniques we've developed to use it. Now that we know that a compile-time itoa() is possible, and even a basic compile-time printf, I can imagine a whole compile-time library.
Oct 27 2005
parent reply "Walter Bright" <newshound digitalmars.com> writes:
"Don Clugston" <dac nospam.com.au> wrote in message
news:djput7$1822$1 digitaldaemon.com...
 I've also made a template that calculates pi at compile time by summing
 a power series. I have hopes that I can make it work to arbitrary
 precision (it only does reals right now).

 Seriously, this compiler is *far* more advanced than the programming
 techniques we've developed to use it. Now that we know that a
 compile-time itoa() is possible, and even a basic compile-time printf,
 I can imagine a whole compile-time library.

This is perfect stuff for a magazine article. If you want to prepare one, I can get you in with the publisher(s).
Oct 28 2005
parent reply Georg Wrede <georg.wrede nospam.org> writes:
Walter Bright wrote:
 "Don Clugston" <dac nospam.com.au> wrote in message 
 news:djput7$1822$1 digitaldaemon.com...
 
 I've also made a template that calculates pi at compile time by
 summing a power series. I have hopes that I can make it work to
 arbitrary precision (it only does reals right now).
 
 Seriously, this compiler is *far* more advanced than the
 programming techniques we've developed to use it. Now that we know
 that a compile-time itoa() is possible, and even a basic
 compile-time printf, I can imagine a whole compile-time library.

This is perfect stuff for a magazine article. If you want to prepare one, I can get you in with the publisher(s).

Guys, this is just awesome! It took Don to show us what Walter has created! I'm getting the impression it's time to throw my meta stuff out the window. ;-(
Oct 29 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 30 Oct 2005 01:38:17 +0300, Georg Wrede wrote:

 Walter Bright wrote:
 "Don Clugston" <dac nospam.com.au> wrote in message 
 news:djput7$1822$1 digitaldaemon.com...
 
 I've also made a template that calculates pi at compile time by
 summing a power series. I have hopes that I can make it work to
 arbitrary precision (it only does reals right now).
 
 Seriously, this compiler is *far* more advanced than the
 programming techniques we've developed to use it. Now that we know
 that a compile-time itoa() is possible, and even a basic
 compile-time printf, I can imagine a whole compile-time library.

This is perfect stuff for a magazine article. If you want to prepare one, I can get you in with the publisher(s).

Guys, this is just awesome! It took Don to show us what Walter has created! I'm getting the impression it's time to throw my meta stuff out the window. ;-(

Ok, I'll expose my ignorance here, but why is this such a fanfare item? Its just a fancy way to declare literals, no? And who needs PI calculated at compile time anyway - just hard code the literal - it ain't changing anytime soon. What am I missing? -- Derek Parnell Melbourne, Australia 30/10/2005 10:01:06 AM
Oct 29 2005
parent reply Don Clugston <dac nospam.com.au> writes:
Derek Parnell wrote:
 On Sun, 30 Oct 2005 01:38:17 +0300, Georg Wrede wrote:
 
 
Walter Bright wrote:

"Don Clugston" <dac nospam.com.au> wrote in message 
news:djput7$1822$1 digitaldaemon.com...


I've also made a template that calculates pi at compile time by
summing a power series. I have hopes that I can make it work to
arbitrary precision (it only does reals right now).

Seriously, this compiler is *far* more advanced than the
programming techniques we've developed to use it. Now that we know
that a compile-time itoa() is possible, and even a basic
compile-time printf, I can imagine a whole compile-time library.

This is perfect stuff for a magazine article. If you want to prepare one, I can get you in with the publisher(s).

Guys, this is just awesome! It took Don to show us what Walter has created! I'm getting the impression it's time to throw my meta stuff out the window. ;-(

Ok, I'll expose my ignorance here, but why is this such a fanfare item? Its just a fancy way to declare literals, no? And who needs PI calculated at compile time anyway - just hard code the literal - it ain't changing anytime soon. What am I missing?

Yes, calculating PI is largely useless, the only reason for using PI is that everyone knows what it should be. But in general, the feature allows you to replace most 'magic numbers' with the program which was used to create them. Even more interesting to me is the possibility of creating lookup tables at compile time. Examples: * the table which is used for isalpha(), ispunct(), etc. * the table of primes in the BigInt library of Deimos. In both these cases, it's obvious that another program was written to generate those tables. Where is it now? Is it bug-free? What if you decide you need a bigger lookup table? With the metaprogramming possibilities in D, we don't need another program. Caveat: right now, lookup tables can only be created using strings to store the data, because ~ is evaluated early only for strings, and [] is never evaluated early. But I think both those limitations are emminently fixable. It's interesting also because it gives most of the capability of languages like LISP, but with syntax that is closer to C, and consequently much more intelligible for C/C++ programmers. It's a case where D is venturing into unexplored territority.
Nov 02 2005
parent reply Sean Kelly <sean f4.ca> writes:
Don Clugston wrote:
 
 It's interesting also because it gives most of the capability of 
 languages like LISP, but with syntax that is closer to C, and 
 consequently much more intelligible for C/C++ programmers.
 It's a case where D is venturing into unexplored territority.

Agreed. Blitz++ is a good example of some practical uses for this sort of thing (http://www.oonumerics.org/blitz/), but this sort of code generation is difficult to impossible with C++. All we need now is built-in support for lists and we'll practically have a real LISP front-end on D :) Sean
Nov 02 2005
parent reply Sean Kelly <sean f4.ca> writes:
Sean Kelly wrote:
 Don Clugston wrote:
 
 It's interesting also because it gives most of the capability of 
 languages like LISP, but with syntax that is closer to C, and 
 consequently much more intelligible for C/C++ programmers.
 It's a case where D is venturing into unexplored territority.

Agreed. Blitz++ is a good example of some practical uses for this sort of thing (http://www.oonumerics.org/blitz/), but this sort of code generation is difficult to impossible with C++. All we need now is built-in support for lists and we'll practically have a real LISP front-end on D :)

I just saw your post in the other forum. That would cover everything but typelists, which would be quite useful for template specialization and such: template fn(T : [int,long]) { void fn() {} } I suppose the declarative form might be: const type t = [int,long]; Though I'm not sure whether being able to declare a typelist makes sense as it would allow things like this: static if( ... ) t[0] var; else char var; Thoughts? Sean
Nov 02 2005
parent reply Don Clugston <dac nospam.com.au> writes:
Sean Kelly wrote:
 Sean Kelly wrote:
 
 Don Clugston wrote:

 It's interesting also because it gives most of the capability of 
 languages like LISP, but with syntax that is closer to C, and 
 consequently much more intelligible for C/C++ programmers.
 It's a case where D is venturing into unexplored territority.

Agreed. Blitz++ is a good example of some practical uses for this sort of thing (http://www.oonumerics.org/blitz/), but this sort of code generation is difficult to impossible with C++. All we need now is built-in support for lists and we'll practically have a real LISP front-end on D :)

I just saw your post in the other forum. That would cover everything but typelists, which would be quite useful for template specialization and such: template fn(T : [int,long]) { void fn() {} } I suppose the declarative form might be: const type t = [int,long]; Though I'm not sure whether being able to declare a typelist makes sense as it would allow things like this: static if( ... ) t[0] var; else char var; Thoughts?

 Sean

Hmmm. That's a definite possible direction. I suspect, though, that it's not as important for D as for Lisp. We already have normal template type parameters, structs, and indexed arrays. I'm a bit worried about painting ourselves into a corner when we don't even know how big the room is yet :-) Another radical possibility: Imagine that itoa!(int) was an allowable overload for itoa(int). The rationale is, the function itoa!(int) is not a template at all. Instead, it's a function that operates on compile-time constants, turning them into other compile-time constants. It should never result in any code being generated in the executable. The minimal syntax change to allow this would be by creating const aliases. const alias itoa!(long n).s char[] itoa(long n); This means: if you see a call to itoa() where the parameter n is known at compile time, you should use the template instead of the normal itoa() function. But the ideal would be to have (say) const int as a type which is distinct from int, having a value which is known at compile time, and potentially treated differently. (This is completely different to const in C++, which doesn't seem to mean much at all). Any such function could be used in compile-time expressions like static assert and static if. So when you write writef( itoa(x) ); and if at compile time x is known to be 7, then this compiles to writef(itoa!(7).s); ----> writef("7"); but if the value of x is unknown (either because it reaches that line at different times with more than one value, or because the compiler can't work it out what the single value is), it just uses itoa(x). I'm sure there are lots of problems with this idea -- I'm just beginning to think about it. I can't see how it could be done with D's simple function overloading.
Nov 02 2005
parent BCS <BCS_member pathlink.com> writes:
How about a function attribute of "state"? It would mean that the return value
of the function is only a function of it's arguments (and it's only side effect
is it's return). This would mean that if a function has the state attribute and
it's arguments are known at compile time, then it's return can also be found at
compile time (more or less what Don Clugston suggested under a different name).
The important aspect of this is that the compiler would only allow code in such
a function that it knows can be evaluated and removed at compile time. This
would also eliminate the use of a template and a function doing the same thing.



In article <dkarbt$2j3$1 digitaldaemon.com>, Don Clugston says...
Sean Kelly wrote:
 Sean Kelly wrote:
 
 Don Clugston wrote:

 It's interesting also because it gives most of the capability of 
 languages like LISP, but with syntax that is closer to C, and 
 consequently much more intelligible for C/C++ programmers.
 It's a case where D is venturing into unexplored territority.

Agreed. Blitz++ is a good example of some practical uses for this sort of thing (http://www.oonumerics.org/blitz/), but this sort of code generation is difficult to impossible with C++. All we need now is built-in support for lists and we'll practically have a real LISP front-end on D :)

I just saw your post in the other forum. That would cover everything but typelists, which would be quite useful for template specialization and such: template fn(T : [int,long]) { void fn() {} } I suppose the declarative form might be: const type t = [int,long]; Though I'm not sure whether being able to declare a typelist makes sense as it would allow things like this: static if( ... ) t[0] var; else char var; Thoughts?

 Sean

Hmmm. That's a definite possible direction. I suspect, though, that it's not as important for D as for Lisp. We already have normal template type parameters, structs, and indexed arrays. I'm a bit worried about painting ourselves into a corner when we don't even know how big the room is yet :-) Another radical possibility: Imagine that itoa!(int) was an allowable overload for itoa(int). The rationale is, the function itoa!(int) is not a template at all. Instead, it's a function that operates on compile-time constants, turning them into other compile-time constants. It should never result in any code being generated in the executable. The minimal syntax change to allow this would be by creating const aliases. const alias itoa!(long n).s char[] itoa(long n); This means: if you see a call to itoa() where the parameter n is known at compile time, you should use the template instead of the normal itoa() function. But the ideal would be to have (say) const int as a type which is distinct from int, having a value which is known at compile time, and potentially treated differently. (This is completely different to const in C++, which doesn't seem to mean much at all). Any such function could be used in compile-time expressions like static assert and static if. So when you write writef( itoa(x) ); and if at compile time x is known to be 7, then this compiles to writef(itoa!(7).s); ----> writef("7"); but if the value of x is unknown (either because it reaches that line at different times with more than one value, or because the compiler can't work it out what the single value is), it just uses itoa(x). I'm sure there are lots of problems with this idea -- I'm just beginning to think about it. I can't see how it could be done with D's simple function overloading.

Nov 02 2005
prev sibling parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Don Clugston wrote:
 I really think the docs for "static if" don't do it justice. Since I'm 
 new to D templates, I've been trying to come up with a more interesting 
 example than the one in the docs. Here's my first attempt. This program 
 prints a nicely-formatted list of the first 30 Fibonnacci numbers _at 
 compile time_.
 Note that except for the clunky 'inttostr' template (which is a general 
 utility template, could be quite useful in other contexts), it's not 
 much longer than a straightforward implementation in a functional 
 programming language.
 
 Enjoy!
 

really nice work. And when that promotion bug is fixed so things can be writen this way: template inttostr(int n) { static if (n<0) const char [] inttostr = "-" ~ .inttostr!(-n); else static if (n<10) const char [] inttostr = digittostr!(n); else const char [] inttostr = .inttostr!(n/10) ~ digittostr!(n%10); } it will be even nicer and easier!
Oct 27 2005
next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Ivan Senji wrote:
 Don Clugston wrote:
 
 I really think the docs for "static if" don't do it justice. Since I'm 
 new to D templates, I've been trying to come up with a more 
 interesting example than the one in the docs. Here's my first attempt. 
 This program prints a nicely-formatted list of the first 30 Fibonnacci 
 numbers _at compile time_.
 Note that except for the clunky 'inttostr' template (which is a 
 general utility template, could be quite useful in other contexts), 
 it's not much longer than a straightforward implementation in a 
 functional programming language.

 Enjoy!

really nice work. And when that promotion bug is fixed so things can be writen this way: template inttostr(int n) { static if (n<0) const char [] inttostr = "-" ~ .inttostr!(-n); else static if (n<10) const char [] inttostr = digittostr!(n); else const char [] inttostr = .inttostr!(n/10) ~ digittostr!(n%10); } it will be even nicer and easier!

Indeed. Even better would be if a constant index of an array of constants, was a literal. Then digittostr would just be template digittostr(int n) { const char [] digittostr="0123456789"[n]; } at which point it becomes a genuine functional programming language, because you can manipulate lists. (Right now I've shown you can create an arbitrary list, but AFAIK you can't peek inside it). But maybe there's some syntax it will accept right now. Walter's created a monster. Who knows what it can do!
Oct 27 2005
next sibling parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Don Clugston wrote:
 Indeed. Even better would be if a constant index of an array of 
 constants, was a literal.
 Then digittostr would just be
 
 template digittostr(int n) {
   const char [] digittostr="0123456789"[n];
 }
 

I would expect this to work instead of getting the odd "non-constant expression "0123456789"[5]" error. But this works :) template digittostr(int n) { const char[] digittostr; static this() { digittostr = "0123456789"[n..n+1]; } } But unfortunately this way we can't have implicit promotion.
 at which point it becomes a genuine functional programming language, 
 because you can manipulate lists. (Right now I've shown you can create 
 an arbitrary list, but AFAIK you can't peek inside it).
 But maybe there's some syntax it will accept right now.
 
 Walter's created a monster. Who knows what it can do!

Indeed!
Oct 27 2005
next sibling parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Ivan Senji wrote:
 
 But this works :)
 

Correction: it works in writefln(digittostr!(0).digittostr); but not at compile time with pragma(msg,digittostr!(0).digittostr); Strange. What does "string expected for message, not 'digittostr'" mean anyway??
 template digittostr(int n)
 {
   const char[] digittostr;
   static this()
   {
     digittostr = "0123456789"[n..n+1];
   }
 }
 
 But unfortunately this way we can't have implicit promotion.

Oct 27 2005
parent reply Don Clugston <dac nospam.com.au> writes:
Ivan Senji wrote:
 Ivan Senji wrote:
 
 But this works :)

Correction: it works in writefln(digittostr!(0).digittostr); but not at compile time with pragma(msg,digittostr!(0).digittostr); Strange. What does "string expected for message, not 'digittostr'" mean anyway??

It means it didn't evaluate it at compile time. We're so close! I've also tried this way: const char q[][] = ["0", "1", "2", "3","4","5","6","7","8","9"]; const char[] digittostr = q[n]; but it gives the string as q[3], not "3". It seems that [] is never evaluated at compile time, but it's fascinating that ~ is.
Oct 27 2005
parent Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Don Clugston wrote:
 Ivan Senji wrote:
 Strange. What does "string expected for message, not 'digittostr'"
 mean anyway??

It means it didn't evaluate it at compile time.

Yes, i figured out this was the problem.
 We're so close!
 

I hope so.
 I've also tried this way:
 
    const char q[][] = ["0", "1", "2", "3","4","5","6","7","8","9"];
    const char[] digittostr = q[n];
 
 but it gives the string as q[3], not "3".
 
 It seems that [] is never evaluated at compile time, but it's 
 fascinating that ~ is.

Yes it is fascinating, i hope this is a bug and not by spec. Maybe to solve this problem a little Walter-intervention is needed?
Oct 27 2005
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <djq9pn$1jo5$1 digitaldaemon.com>, Ivan Senji says...
But this works :)

template digittostr(int n)
{
   const char[] digittostr;
   static this()
   {
     digittostr = "0123456789"[n..n+1];
   }
}

Another surprise. So constants can be initialized at run time via static this? I thought the format always had to be: const T t = val; Interesting. Sean
Oct 27 2005
parent reply Burton Radons <burton-radons smocky.com> writes:
Sean Kelly wrote:
 In article <djq9pn$1jo5$1 digitaldaemon.com>, Ivan Senji says...
 
But this works :)

template digittostr(int n)
{
  const char[] digittostr;
  static this()
  {
    digittostr = "0123456789"[n..n+1];
  }
}

Another surprise. So constants can be initialized at run time via static this? I thought the format always had to be: const T t = val; Interesting.

Colour me surprised, this is an actual intended feature - if a const value isn't given an initialiser, it can be given one in a static constructor. It can also be used for write-once fields in a class: http://www.digitalmars.com/d/attribute.html#const
Nov 01 2005
next sibling parent Derek Parnell <derek psych.ward> writes:
On Tue, 01 Nov 2005 14:26:49 +0000, Burton Radons wrote:

 Colour me surprised, this is an actual intended feature - if a const 
 value isn't given an initialiser, it can be given one in a static 
 constructor.  It can also be used for write-once fields in a class:
 
 http://www.digitalmars.com/d/attribute.html#const

Yeah! I really wanted this feature and I didn't realize it existed either. Sample code: --------------------------- import std.stdio; const int code_A, code_B, code_C, code_D, code_E; static this() { int v = 1; code_A = v++; code_B = v++; code_C = v++; code_D = v++; code_E = v++; } void main() { writefln("%d %d %d %d %d ", code_A, code_B, code_C, code_D, code_E ); } -- Derek (skype: derek.j.parnell) Melbourne, Australia 2/11/2005 10:39:21 AM
Nov 01 2005
prev sibling next sibling parent reply "Kris" <fu bar.com> writes:
Nice! Now if only we could make the assigment at the declaration site, 
rather than just within static-ctors (the compiler could create an implicit 
static-ctor in the same way that it creates module-ctors :-)

e.g. const Foo f = new Foo;

Also new to me was this little nugget at the bottom of the page: "Functions 
declared as abstract can still have function bodies. This is so that even 
though they must be overridden, they can still provide 'base class 
functionality.' " ...  Suuweeeet! When did that happen?

Hats' off to Walter for these two.



"Burton Radons" <burton-radons smocky.com> wrote in message 
news:dk8q3b$136s$1 digitaldaemon.com...
 Sean Kelly wrote:
 In article <djq9pn$1jo5$1 digitaldaemon.com>, Ivan Senji says...

But this works :)

template digittostr(int n)
{
  const char[] digittostr;
  static this()
  {
    digittostr = "0123456789"[n..n+1];
  }
}

Another surprise. So constants can be initialized at run time via static this? I thought the format always had to be: const T t = val; Interesting.

Colour me surprised, this is an actual intended feature - if a const value isn't given an initialiser, it can be given one in a static constructor. It can also be used for write-once fields in a class: http://www.digitalmars.com/d/attribute.html#const

Nov 01 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 1 Nov 2005 15:49:22 -0800, Kris wrote:


 Also new to me was this little nugget at the bottom of the page: "Functions 
 declared as abstract can still have function bodies. This is so that even 
 though they must be overridden, they can still provide 'base class 
 functionality.' " ...  Suuweeeet! When did that happen?

Huh? If they *must* be overridden, how does their function body get executed? In fact, I've tried a few tests and it seems that they do not have to be overridden anyway. Also, it seems that the only thing that 'abstract' does is allow the compiler to issue an error rather than catch it at link time. Sample without 'abstract': --------------------- import std.stdio; class CC_base { void funcX(); } class myClass : CC_base { } void main() { myClass cc = new myClass; cc.funcX(); } ------------------ This compiles okay but crashes at link time " Error 42: Symbol Undefined _D4test7CC_base5funcXFZv". If I put the 'abstract' on the class declaration it has no effect. The linker still aborts. If I put it on the member function "abstract void funcX();" then the compiler detects a problem "cannot create instance of abstract class myClass". If I give the function a body ... abstract void funcX() { writefln("Base functionality"); } it compiles, links and runs okay. The 'abstract' function which is *not* overridden gets run. If I remove the 'abstract' but retain the body there is no change. The 'abstract' doesn't seem to do anything with regards to forcing the function to be overridden. So it seems that if you give an abstract function a body then the 'abstract' is ignored. -- Derek (skype: derek.j.parnell) Melbourne, Australia 2/11/2005 10:52:14 AM
Nov 01 2005
next sibling parent reply "Kris" <fu bar.com> writes:
I haven't tried compiling or running any of these, so please take these 
comments with a grain of salt:

1) In the cases without a function body, the linker is doing the right 
thing. That "no body" syntax states the symbol will be resolved elsewhere. 
You must add "abstract" to the method declaration to get the desired 
behaviour, as you did in the third case.

2) The way I read the abstract documentation is thus: "an abstract method 
can provide basic implementation, which may or may not be taken advantage of 
by an overriding implementation". Hence, a subclass must provide a matching 
& overriding method (upon the superclass abstract method). However, said 
subclass method can simply invoke super.functionName(args) to get the 
default behavior.

If, as you indicate, an abstract method with a body does not actually 
/require/ subclass implementation, then the use of "abstract" on the 
superclass method is entirely superfluous ~ and the documentation claims 
would thus be bogus (since the compiler would behave the same with & without 
the superclass "abstract" method modifer). I sincerely hope this indicates a 
bug in the compiler as opposed to a waft of hot air from the documentation 
;-)

- Kris



"Derek Parnell" <derek psych.ward> wrote in message 
news:nvxauw987u1e$.h217vqke1lia$.dlg 40tude.net...
 On Tue, 1 Nov 2005 15:49:22 -0800, Kris wrote:


 Also new to me was this little nugget at the bottom of the page: 
 "Functions
 declared as abstract can still have function bodies. This is so that even
 though they must be overridden, they can still provide 'base class
 functionality.' " ...  Suuweeeet! When did that happen?

Huh? If they *must* be overridden, how does their function body get executed? In fact, I've tried a few tests and it seems that they do not have to be overridden anyway. Also, it seems that the only thing that 'abstract' does is allow the compiler to issue an error rather than catch it at link time. Sample without 'abstract': --------------------- import std.stdio; class CC_base { void funcX(); } class myClass : CC_base { } void main() { myClass cc = new myClass; cc.funcX(); } ------------------ This compiles okay but crashes at link time " Error 42: Symbol Undefined _D4test7CC_base5funcXFZv". If I put the 'abstract' on the class declaration it has no effect. The linker still aborts. If I put it on the member function "abstract void funcX();" then the compiler detects a problem "cannot create instance of abstract class myClass". If I give the function a body ... abstract void funcX() { writefln("Base functionality"); } it compiles, links and runs okay. The 'abstract' function which is *not* overridden gets run. If I remove the 'abstract' but retain the body there is no change. The 'abstract' doesn't seem to do anything with regards to forcing the function to be overridden. So it seems that if you give an abstract function a body then the 'abstract' is ignored. -- Derek (skype: derek.j.parnell) Melbourne, Australia 2/11/2005 10:52:14 AM

Nov 01 2005
parent reply Bruno Medeiros <daiphoenixNO SPAMlycos.com> writes:
Kris wrote:
 I haven't tried compiling or running any of these, so please take these 
 comments with a grain of salt:
 
 1) In the cases without a function body, the linker is doing the right 
 thing. That "no body" syntax states the symbol will be resolved elsewhere. 
 You must add "abstract" to the method declaration to get the desired 
 behaviour, as you did in the third case.
 

to declare a class method *outside* of the class declaration itself? ( la C++) -- Bruno Medeiros - CS/E student "Certain aspects of D are a pathway to many abilities some consider to be... unnatural."
Nov 02 2005
parent reply "Walter Bright" <newshound digitalmars.com> writes:
"Bruno Medeiros" <daiphoenixNO SPAMlycos.com> wrote in message
news:4368A9E1.5050003 SPAMlycos.com...
 Kris wrote:
 I haven't tried compiling or running any of these, so please take these
 comments with a grain of salt:

 1) In the cases without a function body, the linker is doing the right
 thing. That "no body" syntax states the symbol will be resolved


 You must add "abstract" to the method declaration to get the desired
 behaviour, as you did in the third case.


Yes, it makes it possible to provide the implementation of a class function in another language, such as assembly, C, or even Fortran!
 Is it even possible (at source code level)
 to declare a class method *outside* of the class declaration itself? (
 la C++)

Not in D, but in another language, yes.
Nov 05 2005
next sibling parent pragma <pragma_member pathlink.com> writes:
In article <dkjhqm$2hg2$1 digitaldaemon.com>, Walter Bright says...
"Bruno Medeiros" <daiphoenixNO SPAMlycos.com> wrote in message
 Is it even possible (at source code level)
 to declare a class method *outside* of the class declaration itself? (
 la C++)

Not in D, but in another language, yes.

NOTE: Please, for the love of all that's sane, do not use the following in production code, unless someone puts a gun to your head. Otherwise, Cuthulu himself will awake from his ancient slumber and devour all of mankind, starting with you. :) In the spirit of this thread, I took walter's words here as a direct challenge, as I felt pretty sure D could be put to task here. Technically, Walter is correct. D wasn't designed with partial classes, from within D, in mind. His hint above, that other languages could be used for this facility, got me thinking that this could be done. It is possible to work around this by clever use of the compiler, and tricking the linker. So here is the hack: ////////////////// module test_a; private import std.stdio; class Foobar{ extern(C) uint add(uint a,uint b); // here is your 'empty' method } void main(){ Foobar f = new Foobar(); writefln("test: %d",f.add(42,69)); } /////////////////// module test_b; import test_a; // here is the body of the method (that's not a typeo) extern(C) uint _D6test_a6Foobar3addUkkZk(Foobar _this,uint a,uint b){ return a+b; } ////////////////// Compile: dmd test_a.d test_b.d Run: test_a.exe Output: "test: 111" The trick here is is to use extern(C) as to specify the call-spec on the class-definition (test_a) side, *and* circumvent name-mangling on the method-definition side (test_b). The horribly named symbol you see in test_b is a perfect match for the symbol in test_a, so the linker is satisified and the program runs perfectly. The reason why this works at all is because D does not name-mangle if the symbol defined is at the module level, and does not use the D calling convention. By this, extern(C) has two distinct behaviors, depending on context; this makes sense from a C-programming point of view. Note: I did try using extern(C) *only* on the test_b side (being careful to fetch the last parameter from EAX), but this somehow generates an access violation once main() exits; differences in stack-management between C and D perhaps? Also, you can link the modules one at a time, and ignore the linker errors you get (even when using '-c'); for reasons unknown, these do not interfere with the creation of .obj files one bit. - EricAnderton at yahoo
Nov 05 2005
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Sat, 5 Nov 2005 15:49:02 -0800, Walter Bright wrote:

 "Bruno Medeiros" <daiphoenixNO SPAMlycos.com> wrote in message
 news:4368A9E1.5050003 SPAMlycos.com...
 Kris wrote:
 I haven't tried compiling or running any of these, so please take these
 comments with a grain of salt:

 1) In the cases without a function body, the linker is doing the right
 thing. That "no body" syntax states the symbol will be resolved


 You must add "abstract" to the method declaration to get the desired
 behaviour, as you did in the third case.


Yes, it makes it possible to provide the implementation of a class function in another language, such as assembly, C, or even Fortran!
 Is it even possible (at source code level)
 to declare a class method *outside* of the class declaration itself? (
 la C++)

Not in D, but in another language, yes.

Walter, you did not answer my issue with this. I repeat it from my earlier post... It seems that the only thing that 'abstract' does is allow the compiler to issue an error rather than catch it at link time. The 'abstract' function which is *not* overridden gets run. If I remove the 'abstract' but retain the body there is no change. The 'abstract' doesn't seem to do anything with regards to forcing the function to be overridden. So it seems that if you give an abstract function a body then the 'abstract' is ignored. This is *not* what the documentation seems to be saying. -- Derek Parnell Melbourne, Australia 6/11/2005 6:00:19 PM
Nov 05 2005
prev sibling parent Sean Kelly <sean f4.ca> writes:
Derek Parnell wrote:
 On Tue, 1 Nov 2005 15:49:22 -0800, Kris wrote:
 
 
 
Also new to me was this little nugget at the bottom of the page: "Functions 
declared as abstract can still have function bodies. This is so that even 
though they must be overridden, they can still provide 'base class 
functionality.' " ...  Suuweeeet! When did that happen?


Awesome!
 Huh? If they *must* be overridden, how does their function body get
 executed?

It's similar to this in C++: class C { virtual void fn() = 0 { /* default behavior */ } }; class D : C { virtual void fn() { C::fn(); } };
 In fact, I've tried a few tests and it seems that they do not
 have to be overridden anyway. Also, it seems that the only thing that
 'abstract' does is allow the compiler to issue an error rather than catch
 it at link time.

Perhaps this bit isn't finished yet. It would be nice if this were enforced as per the spec. Sean
Nov 01 2005
prev sibling parent Bruno Medeiros <daiphoenixNO SPAMlycos.com> writes:
Burton Radons wrote:
...
 It can also be used for write-once fields in a class:
 
 http://www.digitalmars.com/d/attribute.html#const

Hum... thus, that's a case where it's not true that "The const attribute declares constants that can be evaluated at compile time", right? -- Bruno Medeiros - CS/E student "Certain aspects of D are a pathway to many abilities some consider to be... unnatural."
Nov 02 2005
prev sibling parent Sean Kelly <sean f4.ca> writes:
In article <djq81i$1i11$1 digitaldaemon.com>, Don Clugston says...
Indeed. Even better would be if a constant index of an array of 
constants, was a literal.
Then digittostr would just be

template digittostr(int n) {
   const char [] digittostr="0123456789"[n];
}

at which point it becomes a genuine functional programming language, 
because you can manipulate lists. (Right now I've shown you can create 
an arbitrary list, but AFAIK you can't peek inside it).
But maybe there's some syntax it will accept right now.

This would be extremely cool. And it seems like it shouldn't be terribly difficult to implement, since ~ is already supported in this context. Sean
Oct 27 2005
prev sibling parent reply "Walter Bright" <newshound digitalmars.com> writes:
"Ivan Senji" <ivan.senji_REMOVE_ _THIS__gmail.com> wrote in message
news:djq3r9$1dfh$1 digitaldaemon.com...
 And when that promotion bug is fixed so things can be
 writen this way:

I'm not familiar with that bug?
Oct 28 2005
parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Walter Bright wrote:
 "Ivan Senji" <ivan.senji_REMOVE_ _THIS__gmail.com> wrote in message
 news:djq3r9$1dfh$1 digitaldaemon.com...
 
And when that promotion bug is fixed so things can be
writen this way:

I'm not familiar with that bug?

Sorry not a bug, you said it was a limitation. template factorial(int n) { static if (1) { enum { factorial = n * .factorial!(n-1) } } } No promotion when using static if.
Oct 28 2005
parent reply "Walter Bright" <newshound digitalmars.com> writes:
"Ivan Senji" <ivan.senji_REMOVE_ _THIS__gmail.com> wrote in message
news:dju3t8$2ida$1 digitaldaemon.com...
 Sorry not a bug, you said it was a limitation.

 template factorial(int n)
 {
    static if (1) {
        enum { factorial = n * .factorial!(n-1) }
    }
 }

 No promotion when using static if.

I'm still not sure what you mean. Can you refresh my memory?
Oct 28 2005
parent reply Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
Walter Bright wrote:
 "Ivan Senji" <ivan.senji_REMOVE_ _THIS__gmail.com> wrote in message
 news:dju3t8$2ida$1 digitaldaemon.com...
 
Sorry not a bug, you said it was a limitation.

template factorial(int n)
{
   static if (1) {
       enum { factorial = n * .factorial!(n-1) }
   }
}

No promotion when using static if.

I'm still not sure what you mean. Can you refresh my memory?

Sorry again, i don't know why i called it promotion, i should have said implicit template properties. Simpler example: template Foo(int n) { static if(n==0) const int Foo; else static if(n==1) const float Foo; else const char[] Foo; } writefln(Foo!(1).Foo); //works writefln(Foo!(1)); //voids have no value
Oct 29 2005
parent "Walter Bright" <newshound digitalmars.com> writes:
"Ivan Senji" <ivan.senji_REMOVE_ _THIS__gmail.com> wrote in message
news:djv7p7$gkq$1 digitaldaemon.com...
 Sorry again, i don't know why i called it promotion, i should have said
 implicit template properties. Simpler example:

 template Foo(int n)
 {
    static if(n==0) const int Foo;
    else static if(n==1) const float Foo;
    else const char[] Foo;
 }

 writefln(Foo!(1).Foo); //works
 writefln(Foo!(1));     //voids have no value

Ok, I understand now. Don also posted this issue.
Oct 29 2005