www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Static Const (Compile-Time) Functions

reply AJG <AJG_member pathlink.com> writes:
Hi there,

In the vein of the const parameter discussion, I would like to introduce this
topic. Since some form of const is coming, it would be great if it was made to
support static const functions at some point.

Anyway, these functions run at compile time. They can return a true const which
would be identical to a const literal. I don't think they exist in C++, but C++
does have plain const functions. This is not the same thing (it's better). This
is essentially an extremely safe, full-featured macro. For example:

# static const int square(const int num) {
#     return (num * num);
# }
#
# static const char tolower(const char c) {
#     return ((c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c);     
# }
#
# const int foo = square(5);
# // foo = 25 at compile-time.
# const char bar = tolower('B');
# // bar = 'b' at compile-time.

I'm not suggesting any specific syntax, just the general idea. Is this something
that could be done?

Some benefits:
- It doesn't add _anything_ to the runtime.
- Doesn't require any runtime or library support.
- Can make your code faster and smaller by shifting some processing to
compile-time.
- No new concepts or keywords needed. Functions and consts already exist in D.
This simply combines the two.
- Can make your code cleaner and more readable without incurring any
function-call penalties.
- You get the best of both worlds: macro zero-cost efficiency, plus complete
function safety and (hehehe) functionality.


Thanks,
--AJG.
Jul 15 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:db8r7q$2k3u$1 digitaldaemon.com...

Something like this can already be done with templates, although with a 
more.. obtuse syntax.

template square(T, T value)
{
 const T square=(value*value);
}

void main()
{
 int x=square!(int,5);
 writefln(x);
}

Since the template is expanded at compile-time, the constant folds, and 
x=25.  The square!() template can also be used to initialize global or const 
members.

A more function-esque syntax would be welcome, though. 
Jul 15 2005
next sibling parent reply AJG <AJG_member pathlink.com> writes:
Hm... interesting. I didn't know you could do that. Thanks for the suggestion.

The problem is (other than being template-syntax-clunky), that you are still
limited to one initialization. In essense you are just moving it somewhere else.

I'll definitely use it, but it'd be great to have real const functions with
normal syntax and features. Walter, before I get my hopes up, is this even
technically possible?

--AJG.

In article <db8ssg$2mjj$1 digitaldaemon.com>, Jarrett Billingsley says...
"AJG" <AJG_member pathlink.com> wrote in message 
news:db8r7q$2k3u$1 digitaldaemon.com...

Something like this can already be done with templates, although with a 
more.. obtuse syntax.

template square(T, T value)
{
 const T square=(value*value);
}

void main()
{
 int x=square!(int,5);
 writefln(x);
}

Since the template is expanded at compile-time, the constant folds, and 
x=25.  The square!() template can also be used to initialize global or const 
members.

A more function-esque syntax would be welcome, though. 

Jul 15 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:db9o29$gij$1 digitaldaemon.com...
 The problem is (other than being template-syntax-clunky), that you are 
 still
 limited to one initialization. In essense you are just moving it somewhere 
 else.

Nope. You can use it as many times as you'd like. I use this weird form of templates to replace function-style macros from C headers that are used to initialize module-level variables or constants. const int x=square!(int,5); const int y=square!(int,10); const float z=square!(float,3.14159);
 I'll definitely use it, but it'd be great to have real const functions 
 with
 normal syntax and features. Walter, before I get my hopes up, is this even
 technically possible?

 --AJG.

 In article <db8ssg$2mjj$1 digitaldaemon.com>, Jarrett Billingsley says...
"AJG" <AJG_member pathlink.com> wrote in message
news:db8r7q$2k3u$1 digitaldaemon.com...

Something like this can already be done with templates, although with a
more.. obtuse syntax.

template square(T, T value)
{
 const T square=(value*value);
}

void main()
{
 int x=square!(int,5);
 writefln(x);
}

Since the template is expanded at compile-time, the constant folds, and
x=25.  The square!() template can also be used to initialize global or 
const
members.

A more function-esque syntax would be welcome, though.


Jul 15 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

"AJG" <AJG_member pathlink.com> wrote in message 
news:db9o29$gij$1 digitaldaemon.com...
 The problem is (other than being template-syntax-clunky), that you are 
 still
 limited to one initialization. In essense you are just moving it somewhere 
 else.

Nope. You can use it as many times as you'd like. I use this weird form of templates to replace function-style macros from C headers that are used to initialize module-level variables or constants.

No, I meant that the template itself is just one initialization. There is no other useful construct that you could use (e.g. a static if). I mean, the whole thing has to be one single assignment expression, right? --AJG.
const int x=square!(int,5);
const int y=square!(int,10);
const float z=square!(float,3.14159);

 I'll definitely use it, but it'd be great to have real const functions 
 with
 normal syntax and features. Walter, before I get my hopes up, is this even
 technically possible?

 --AJG.

 In article <db8ssg$2mjj$1 digitaldaemon.com>, Jarrett Billingsley says...
"AJG" <AJG_member pathlink.com> wrote in message
news:db8r7q$2k3u$1 digitaldaemon.com...

Something like this can already be done with templates, although with a
more.. obtuse syntax.

template square(T, T value)
{
 const T square=(value*value);
}

void main()
{
 int x=square!(int,5);
 writefln(x);
}

Since the template is expanded at compile-time, the constant folds, and
x=25.  The square!() template can also be used to initialize global or 
const
members.

A more function-esque syntax would be welcome, though.



Jul 15 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:dba07g$ll2$1 digitaldaemon.com...
 No, I meant that the template itself is just one initialization. There is 
 no
 other useful construct that you could use (e.g. a static if). I mean, the 
 whole
 thing has to be one single assignment expression, right?

Ah, yes, I see what you mean. You wouldn't be able to, say, calculate a factorial with this (though that can strangely be done with templates as well..). I'm not sure how complex it would be, however, to evaluate compile-time functions, as they'd have to be compiled and run _during_ the compilation process. I don't know if that's even possible.
Jul 17 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

Ah, yes, I see what you mean.  You wouldn't be able to, say, calculate a 
factorial with this (though that can strangely be done with templates as 
well..).

Yeah, I'm finding out more and more exotic uses for templates... this would all get much better if implicit instantiation were possible.
I'm not sure how complex it would be, however, to evaluate compile-time 
functions, as they'd have to be compiled and run _during_ the compilation 
process.  I don't know if that's even possible. 

I have a feeling this is related to the halting problem. However, I don't think it's hopeless. I think you would only have to deal with a small, limited subset of all possible inputs (namely, constant inputs). Can anyone who knows about this pitch in? Thanks, --AJG.
Jul 17 2005
parent reply BCS <BCS_member pathlink.com> writes:
In article <dbeh1t$12p4$1 digitaldaemon.com>, AJG says...
..

Yeah, I'm finding out more and more exotic uses for templates... this would all
get much better if implicit instantiation were possible. 

..
--AJG.

While we are whishing, how about implicit instantiation defining the return type of a function as a function of the inputs. That would allow something like this: #// a Unit (m, km, kg, lb, etc.) safe arithmetic type # #template Unit(T, int d, int m, int t) #{ # typedef T Unit; # # Unit!(T,d,m,t).Unit opAdd(Unit!(T,d,m,t).Unit v1, Unit!(T,d,m,t).Unit v2) # { # return csat(T)v1 + cast(T)v2; # } # Unit!(T,d,m,t).Unit opSub(Unit!(T,d,m,t).Unit v1, Unit!(T,d,m,t).Unit v2) # { # return cast(T)v1 - cast(T)v2; # } # # Unit!(T,d1+d2,m1+m2,t1+t2).Unit opMul(Unit!(T,d1,m1,t1).Unit v1, Unit!(T,d2,m2,t2).Unit v2) # { # return cast(T)v1 * cast(T)v2; # } # # Unit!(T,d1-d2,m1-m2,t1-t2).Unit opMul(Unit!(T,d1,m1,t1).Unit v1, Unit!(T,d2,m2,t2).Unit v2) # { # return cast(T)v1 / cast(T)v2; # } #} I know, I know, implementing this would likely be a royal mess. One possible means to simplify it would be to require a list of allowed instantiations e.g. # instance Unit !(real, 1, 0, 0) ; // meters # instance Unit !(real, 0, 1, 0) ; // kg # instance Unit !(real, 0, 0, 1) ; // seconds # instance Unit !(real, 1, 0, -1) ; // m/s # // etc… BCS p.s One quirk of the above example is that it can optimize to the same code you would get with using just reals.
Jul 18 2005
parent reply David Medlock <noone nowhere.com> writes:
BCS wrote:
 In article <dbeh1t$12p4$1 digitaldaemon.com>, AJG says...
 ..
 
 
Yeah, I'm finding out more and more exotic uses for templates... this would all
get much better if implicit instantiation were possible. 

..
--AJG.

While we are whishing, how about implicit instantiation defining the return type of a function as a function of the inputs. That would allow something like this: #// a Unit (m, km, kg, lb, etc.) safe arithmetic type # #template Unit(T, int d, int m, int t) #{ # typedef T Unit; # # Unit!(T,d,m,t).Unit opAdd(Unit!(T,d,m,t).Unit v1, Unit!(T,d,m,t).Unit v2) # { # return csat(T)v1 + cast(T)v2; # } # Unit!(T,d,m,t).Unit opSub(Unit!(T,d,m,t).Unit v1, Unit!(T,d,m,t).Unit v2) # { # return cast(T)v1 - cast(T)v2; # } # # Unit!(T,d1+d2,m1+m2,t1+t2).Unit opMul(Unit!(T,d1,m1,t1).Unit v1, Unit!(T,d2,m2,t2).Unit v2) # { # return cast(T)v1 * cast(T)v2; # } # # Unit!(T,d1-d2,m1-m2,t1-t2).Unit opMul(Unit!(T,d1,m1,t1).Unit v1, Unit!(T,d2,m2,t2).Unit v2) # { # return cast(T)v1 / cast(T)v2; # } #} I know, I know, implementing this would likely be a royal mess. One possible means to simplify it would be to require a list of allowed instantiations e.g. # instance Unit !(real, 1, 0, 0) ; // meters # instance Unit !(real, 0, 1, 0) ; // kg # instance Unit !(real, 0, 0, 1) ; // seconds # instance Unit !(real, 1, 0, -1) ; // m/s # // etc… BCS p.s One quirk of the above example is that it can optimize to the same code you would get with using just reals.

Would it not be easier just to store the value as one quantity, say meters and use member functions to store/retrieve? I do something like this in my programs already, and its more readable. struct Length // stored in meters { float units = 0; float meters() { return units; } float meters( float f ) { return units = f; } float feet() { return meters * FeetPerMeter; } float feet( float v ) { return units = ( v / FeetPerMeter ); } ... Length opAdd( Length L ) { Length tmp; tmp.units = this.units + L.units; return tmp; } } Length a,b,c; a.feet = 20; b.meters = 15; c = a + b; -DavidM
Jul 19 2005
parent reply BCS <BCS_member pathlink.com> writes:
Would it not be easier just to store the value as one quantity, say 
meters and use member functions to store/retrieve?

I do something like this in my programs already, and its more readable.

struct Length // stored in meters
{
   float units = 0;
   float meters() { return units; }
   float meters( float f ) { return units = f; }

   float feet() { return meters * FeetPerMeter; }
   float feet( float v ) { return units = ( v / FeetPerMeter ); }
   ...
   Length opAdd( Length L ) {
	Length tmp;
	tmp.units = this.units + L.units;
	return tmp;
   }
}

Length a,b,c;
a.feet = 20;
b.meters = 15;
c = a + b;

-DavidM

Yes, that would be more convenient if I was only dealing with distances. However it would allow something like the following to compile: Distance a, b, c; a.meters = 15; b.Newtons = 5 c = a + b; Meters plus Newtons is not only meaningless, it is invalid. The template construct that I suggested, would ensure that only Unit of similar types can be added. It would also ensure that all other math operations result in the right type of unit. One solution that I have considered acutely implementing is vary similar to what I suggested, but done as a class with all of the checks done at runtime and the dimensions carried along with the value in an object. It would have significant runtime overhead where as the template method would run as fast as using just bare reals.
Jul 19 2005
next sibling parent reply David Medlock <noone nowhere.com> writes:
BCS wrote:
Would it not be easier just to store the value as one quantity, say 
meters and use member functions to store/retrieve?

I do something like this in my programs already, and its more readable.

struct Length // stored in meters
{
  float units = 0;
  float meters() { return units; }
  float meters( float f ) { return units = f; }

  float feet() { return meters * FeetPerMeter; }
  float feet( float v ) { return units = ( v / FeetPerMeter ); }
  ...
  Length opAdd( Length L ) {
	Length tmp;
	tmp.units = this.units + L.units;
	return tmp;
  }
}

Length a,b,c;
a.feet = 20;
b.meters = 15;
c = a + b;

-DavidM

Yes, that would be more convenient if I was only dealing with distances. However it would allow something like the following to compile: Distance a, b, c; a.meters = 15; b.Newtons = 5 c = a + b; Meters plus Newtons is not only meaningless, it is invalid. The template construct that I suggested, would ensure that only Unit of similar types can be added. It would also ensure that all other math operations result in the right type of unit.

Why not make a Force struct for Newtons? Then they cannot be added together. The type system already supports this type of checking. As a matter of fact you could do things like: Velocity v; Time t; Distance d; d.meters = 100; t.seconds = 10; v = m / t;
 One solution that I have considered acutely implementing is vary similar to
what
 I suggested, but done as a class with all of the checks done at runtime and the
 dimensions carried along with the value in an object. It would have significant
 runtime overhead where as the template method would run as fast as using just
 bare reals.
 

I dont see your solution being any faster, except for possible constants(compile time). My above code will optimize down to just reals. -DavidM
Jul 19 2005
parent BCS <BCS_member pathlink.com> writes:
In article <dbjigh$hva$1 digitaldaemon.com>, David Medlock says...
Why not make a Force struct for Newtons?  Then they cannot be added 
together.  The type system already supports this type of checking.

see my last post (they crossed in cyber space)
 One solution that I have considered acutely implementing is vary similar to 
 what I suggested, but done as a class with all of the checks done at runtime 
 and the dimensions carried along with the value in an object. It would have 
 significant runtime overhead where as the template method would run as fast 
 as using just bare reals.
 


I dont see your solution being any faster, except for possible 
constants(compile time).  My above code will optimize down to just reals.

-DavidM

The class solution would be slower (but would allow runtime units). As to the template solution, its advantage over the structs is not speed (I agree they would run at the same speed) but implementation. It is one typedef where the structs would require anywhere from about 5 or 6 to upwards of 50 different types of structs depending on what you are doing. On the other hand it requires a lot more from the compiler.
Jul 19 2005
prev sibling parent BCS <BCS_member pathlink.com> writes:
In article <dbjeca$ejm$1 digitaldaemon.com>, BCS says...
Yes, that would be more convenient if I was only dealing with distances. 

I just looked again at what you proposed and noticed that it explicitly deals with length. My bad, (:P) that makes about half of my last post junk. On the other hand, that solution requires a different struct to be declared for each type of unit that is going to be used. This vary quickly gets impractical, one quick google search found a list of about twenty categories of BASIC units, not even getting into things like the units of the various constants. Addition is bad enough, but multiplication quickly turns into a nightmare. For things that don’t use to many units, using structs would be just fine, however I tend to end up using more complicated units and would like something a bit more flexible
Jul 19 2005
prev sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:db8ssg$2mjj$1 digitaldaemon.com...
 "AJG" <AJG_member pathlink.com> wrote in message 
 news:db8r7q$2k3u$1 digitaldaemon.com...

 Something like this can already be done with templates, although with a 
 more.. obtuse syntax.

 template square(T, T value)
 {
 const T square=(value*value);
 }

 void main()
 {
 int x=square!(int,5);
 writefln(x);
 }

 Since the template is expanded at compile-time, the constant folds, and 
 x=25.  The square!() template can also be used to initialize global or 
 const members.

 A more function-esque syntax would be welcome, though.

Nice idea!
Jul 15 2005
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Andrew Fedoniouk" <news terrainformatica.com> wrote in message 
news:dba2cb$nu6$1 digitaldaemon.com...
 Nice idea!

Thank Andy Friesen, he's the one who showed me how to do this :) Templates are weird.
Jul 17 2005