Thread overview
Forcing compile time evaluation of pure functions
Jun 30, 2011
scarrow
Jun 30, 2011
scarrow
Jun 30, 2011
bearophile
Jul 15, 2011
scarrow
Jul 15, 2011
bearophile
Jul 15, 2011
bearophile
Jun 30, 2011
Simen Kjaeraas
June 30, 2011
Hey all,

I'd like to embed hashed strings into my code.  The C++ version of this engine ran an external tool to preprocess the files.  In D my strongly pure function is only evaluated if I assign it to something like an enum or invoke it from a template.  So the following generate compile time hashes:

    enum blort = Hash("foo");
    f(CHash!("foo"));

Annoyingly, however, I can't do the following at compile time:

    f(Hash("foo"));

I'm not sure what the point is in distinguishing between these two cases.  If it is a properly pure function there should be no harm in doing a compile time evaluation.  Ideally (and maybe this is being purposely avoided) I want to be able to write:

    string s = "bar";
    f(Hash("foo"));    // invoke the compile time version and pass a constant to f
    f(Hash(s));    // invoke the runtime version and pass the result to f
June 30, 2011
Oh, I also wanted to mention that although ideally I'd be able to invoke the compile time and runtime versions identically and have it automatically select, I'm also worried about accidentally invoking the runtime version when I should have been using the compile time version.  I can't think of a way to make it:

    string s = "foo";
    auto h = Hash("foo");    // I'd like it to fail but doesn't
    auto i = CHash!("foo");  // works fine (compile time)
    auto j = Hash(s);        // works fine (runtime)
    auto k = CHash!(s);      // fails as it should


June 30, 2011
scarrow:

> Annoyingly, however, I can't do the following at compile time:
> 
>     f(Hash("foo"));
> 
> I'm not sure what the point is in distinguishing between these two cases.

Walter has decided that he doesn't like the D compiler to arbitrary run at compile time arbitrary long to run code. This is named partial compilation and it has some traps. In theory I agree with you that I'd like the compiler to be smarter, but in practice Walter prefers control here, so DMD runs compile-time functions only if you explicitly ask their result to be known at compile-time.

Note that in D you are able to run at compile time functions that are pure just in the execution path that's used at compile-time, even if on the whole they are not pure. This works:

int x;
int foo(bool b, int y) { if (b) return x; else return y; }
enum result = foo(false, 10);


> I want to be able to write:
> 
>     string s = "bar";
>     f(Hash("foo"));    // invoke the compile time version and pass a constant to f
>     f(Hash(s));    // invoke the runtime version and pass the result to f

Is this good enough?

/**
To execute a function at compile time.
*/
template StaticEval(A...) {
   enum typeof(A[0]) StaticEval = A[0];
}

void f(size_t) {}
size_t Hash(string s) { return 0U; }

void main() {
    f(Hash("foo"));
    f(StaticEval!(Hash("foo")));
}


The second Hash runs at compile-time, there is only one call to _D4test4HashFAyaZk (compiled with -O -release, without -inline):

__Dmain comdat
L0:     push    EAX
        push    dword ptr FLAT:_DATA[0Ch]
        push    dword ptr FLAT:_DATA[08h]
        call    near ptr _D4test4HashFAyaZk
        call    near ptr _D4test1fFkZv
        xor EAX,EAX
        call    near ptr _D4test1fFkZv
        xor EAX,EAX
        pop ECX
        ret

Bye,
bearophile
June 30, 2011
On Thu, 30 Jun 2011 07:11:44 +0200, scarrow <shawn.baird@gmail.com> wrote:

> Hey all,
>
> I'd like to embed hashed strings into my code.  The C++ version of this engine
> ran an external tool to preprocess the files.  In D my strongly pure function
> is only evaluated if I assign it to something like an enum or invoke it from a
> template.  So the following generate compile time hashes:
>
>     enum blort = Hash("foo");
>     f(CHash!("foo"));
>
> Annoyingly, however, I can't do the following at compile time:
>
>     f(Hash("foo"));
>
> I'm not sure what the point is in distinguishing between these two cases.  If
> it is a properly pure function there should be no harm in doing a compile time
> evaluation.

Actually, there might. Pure functions are allowed to depend upon the
immutable global variables, whose values may be calculated at startup
(see static this)

-- 
  Simen
July 15, 2011
I think invoking a template to call the pure function (StaticEval!(Hash("foo"))
isn't much different from StaticHash!("foo").  You still have to explicitly know
whether you're dealing with a compile time constant or not.  I'd really like to
figure out how to have Hash("foo") be static and Hash(variable) be dynamic.
July 15, 2011
scarrow:

> I'd really like to figure out how to have Hash("foo") be static and Hash(variable) be dynamic.

In GCC there is a way to (sometimes) do it, see the __builtin_constant_p here: http://www.delorie.com/gnu/docs/gcc/gcc_81.html

Time ago I have asked for something similar in D too, because I think it allows to implement a poor's man manual "partial compilation" using templates. Maybe it's worth asking about this in the main D newsgroup. But before asking, it's better to have few use cases examples :-)

Bye,
bearophile
July 15, 2011
> In GCC there is a way to (sometimes) do it, see the __builtin_constant_p here: http://www.delorie.com/gnu/docs/gcc/gcc_81.html

Google code search gives about 8,100 answers: http://www.google.com/codesearch#search/&q=%22__builtin_constant_p%22&type=cs

Bye,
bearophile