Thread overview
Question about Template
Jun 23, 2012
Michael
Jun 23, 2012
simendsjo
Jun 23, 2012
Ali Çehreli
Jun 24, 2012
Philippe Sigaud
Jun 27, 2012
Michael
June 23, 2012
Hello!
  I have a little question about templates. I am trying to write a template "timer" that takes a function and times how long the function takes to execute. Something like
  auto time = timer!sort(array);
is equivalent to
  auto start = Clock.currStdTime();
  sort(array);
  auto time = Clock.currStdTime() - time;

except of course that the variable "start" is not created in the former case.

The closest I came to the correct template is
template(alias F) timer {
  auto timer {
    auto start = Clock.currStdTime();
    F();
    return Clock.currStdTime() - time;
  }
}

The problem with the template is I cannot pass any argument into "F". How should I fix this problem?
Also, I am not entirely sure why I need to write "alias F" instead of just "F", any explanation would be appreciated.

Thank you guys,
Michael
June 23, 2012
On Sat, 23 Jun 2012 17:30:01 +0200, Michael <pongad@gmail.com> wrote:

> Hello!
>    I have a little question about templates. I am trying to write a template "timer" that takes a function and times how long the function takes to execute. Something like
>    auto time = timer!sort(array);
> is equivalent to
>    auto start = Clock.currStdTime();
>    sort(array);
>    auto time = Clock.currStdTime() - time;
>
> except of course that the variable "start" is not created in the former case.
>
> The closest I came to the correct template is
> template(alias F) timer {
>    auto timer {
>      auto start = Clock.currStdTime();
>      F();
>      return Clock.currStdTime() - time;
>    }
> }
>
> The problem with the template is I cannot pass any argument into "F". How should I fix this problem?
> Also, I am not entirely sure why I need to write "alias F" instead of just "F", any explanation would be appreciated.
>
> Thank you guys,
> Michael


import std.datetime;
import std.traits;
import std.stdio;

struct TimerResult(T) {
    TickDuration duration;
    T result;
}

auto timer(alias fn, Args...)(Args args) {
    auto sw = StopWatch(AutoStart.yes);
    auto res = fn(args);
    return TimerResult!(typeof(res))(sw.peek, res);
}

T f(T)(T[] arr) {
    import core.thread;
    Thread.sleep(dur!"seconds"(1));
    return arr[0];
}

void main() {
    auto res = timer!f([1,2,3,4]);
    writeln("first elem: ", res.result);
    writeln("time used: ", res.duration.msecs);
}
June 23, 2012
On 06/23/2012 08:30 AM, Michael wrote:

> Also, I am not entirely sure why I need to write "alias F" instead of
> just "F", any explanation would be appreciated.

F alone would mean a 'type', for example to be used in the template as

    F var;

Ali

June 24, 2012
> The closest I came to the correct template is
> template(alias F) timer {
>  auto timer {
>    auto start = Clock.currStdTime();
>    F();
>    return Clock.currStdTime() - time;
>  }
> }

> The problem with the template is I cannot pass any argument into "F". How
should I fix this problem?

As Simendsjo said, you can use a function template to get what you want and a nicer syntax as the same time.

Slightly different code can be obtained by starting from the above code:

template timer(alias F) // capture F's name
{
    auto timer(Args...)(Args args) // there are your parameters
     {
        import std.datetime; // inner imports
        auto start = Clock.currStdTime();
        F(args);
        return Clock.currStdTime() - start;
    }
}

So it's a template that gets expanded into another template with the same name, a function.

Note that the construct:

template (T...)
{
    T foo(T t) // some function that depends on the template parameters
    {
    }
}

can get syntax sugar like this:

T foo(T...)(T t)
{
}

you can 'fuse' the external template and the inner code to get a function template. The same for classes and structs.

Now, for something a bit more hairy: Simendsjo's version and mine are not strictly equivalent:

- his version is simpler to type and to reason about. You should use it,
it's the D way to do this kind of thing.
- mine (the two level templates) gets an interesting effect: I can use the
first level only (getting the function name), keeping the second level for
later:

// mine
alias timer!foo tfoo; // tfoo is the inner template, the function template
/* a bit later */
tfoo(args1);
tfoo(args2);

import std.algorithm;
Args[] argsArray;
auto m = map!(tfoo)(argsArray); // applying tfoo on all arguments groups

// Simendsjo:
timer!foo(args1);
timer!foo(args2); // You must provide 'foo' for each call.


>
> Also, I am not entirely sure why I need to write "alias F" instead of
just "F", any explanation would be appreciated.

Because in your case, you want to get the name, the symbol. So use an alias parameter. 'F' would be to deal with types, as Ali said.

For example:

auto foo(F, Args...)(F fn, Args args)
{
...
}

in this case, fn is passed as a runtime value of type F:

timer(&foo, args); // the compiler automatically deduces the types of fn
and args.

Philippe


June 27, 2012
Thank you all of you for your help!

Michael

On Sunday, 24 June 2012 at 06:49:38 UTC, Philippe Sigaud wrote:
>> The closest I came to the correct template is
>> template(alias F) timer {
>>  auto timer {
>>    auto start = Clock.currStdTime();
>>    F();
>>    return Clock.currStdTime() - time;
>>  }
>> }
>
>> The problem with the template is I cannot pass any argument into "F". How
> should I fix this problem?
>
> As Simendsjo said, you can use a function template to get what you want and
> a nicer syntax as the same time.
>
> Slightly different code can be obtained by starting from the above code:
>
> template timer(alias F) // capture F's name
> {
>     auto timer(Args...)(Args args) // there are your parameters
>      {
>         import std.datetime; // inner imports
>         auto start = Clock.currStdTime();
>         F(args);
>         return Clock.currStdTime() - start;
>     }
> }
>
> So it's a template that gets expanded into another template with the same
> name, a function.
>
> Note that the construct:
>
> template (T...)
> {
>     T foo(T t) // some function that depends on the template parameters
>     {
>     }
> }
>
> can get syntax sugar like this:
>
> T foo(T...)(T t)
> {
> }
>
> you can 'fuse' the external template and the inner code to get a function
> template. The same for classes and structs.
>
> Now, for something a bit more hairy: Simendsjo's version and mine are not
> strictly equivalent:
>
> - his version is simpler to type and to reason about. You should use it,
> it's the D way to do this kind of thing.
> - mine (the two level templates) gets an interesting effect: I can use the
> first level only (getting the function name), keeping the second level for
> later:
>
> // mine
> alias timer!foo tfoo; // tfoo is the inner template, the function template
> /* a bit later */
> tfoo(args1);
> tfoo(args2);
>
> import std.algorithm;
> Args[] argsArray;
> auto m = map!(tfoo)(argsArray); // applying tfoo on all arguments groups
>
> // Simendsjo:
> timer!foo(args1);
> timer!foo(args2); // You must provide 'foo' for each call.
>
>
>>
>> Also, I am not entirely sure why I need to write "alias F" instead of
> just "F", any explanation would be appreciated.
>
> Because in your case, you want to get the name, the symbol. So use an alias
> parameter. 'F' would be to deal with types, as Ali said.
>
> For example:
>
> auto foo(F, Args...)(F fn, Args args)
> {
> ...
> }
>
> in this case, fn is passed as a runtime value of type F:
>
> timer(&foo, args); // the compiler automatically deduces the types of fn
> and args.
>
> Philippe