Thread overview
Aliasing a mixin (or alternative ways to profile a scope)
Mar 07, 2019
Simon
Mar 07, 2019
Adam D. Ruppe
Mar 07, 2019
Johannes Loher
Mar 07, 2019
Simon
Mar 07, 2019
Johannes Loher
Mar 07, 2019
Johannes Loher
Mar 08, 2019
Simon
Mar 09, 2019
Dennis
Mar 10, 2019
Simon
March 07, 2019
Hello,

I am currently porting the frontend of my instrumenting profiler to D. It features a C++-makro that profiles the time between entering and leaving a scope (achieved with con-/destructors in C++). Since D has scopeguards, I hoped to achieve this by creating a mixin that generates the following code:

measure("func1");
scope(exit) measure("func1");

Since I of course don't want to type a mixin for that everytime I want to use it, I tried to alias it:

import std.meta : Alias;
alias profile_scope(string name) = Alias!(mixin("measure(\"" ~ name ~ "\"); scope(exit) measure(\"" ~ name ~ "\");"));

I expected this to generate the code above by typing profile_scope("func1") in my programm. However the compiler (dmd) gives me the following error:

source\main.d(24): Error: template plattform.profile_scope cannot deduce function from argument types !()(string), candidates are:
source\plattform.d(262):        plattform.profile_scope(string name)

This looks like a deduction/overload kind of error, which has me completly confused since there is no other identifier of the name "profile_scope" in my programm and the error message shows only one candidate.

Is there any way I can achive generating those two statements using only something that effectively looks like a function call/C-macro with an argument?
March 07, 2019
On Thursday, 7 March 2019 at 20:07:27 UTC, Simon wrote:
> measure("func1");
> scope(exit) measure("func1");

I would suggest just using a struct. Make its constructor do the first measure, and its destructor do the second measure.

I betcha you can avoid mixin entirely.
March 07, 2019
Am 07.03.19 um 21:07 schrieb Simon:
> Hello,
> 
> I am currently porting the frontend of my instrumenting profiler to D. It features a C++-makro that profiles the time between entering and leaving a scope (achieved with con-/destructors in C++). Since D has scopeguards, I hoped to achieve this by creating a mixin that generates the following code:
> 
> measure("func1");
> scope(exit) measure("func1");
> 
> Since I of course don't want to type a mixin for that everytime I want to use it, I tried to alias it:
> 
> import std.meta : Alias;
> alias profile_scope(string name) = Alias!(mixin("measure(\"" ~ name ~
> "\"); scope(exit) measure(\"" ~ name ~ "\");"));
> 
> I expected this to generate the code above by typing
> profile_scope("func1") in my programm. However the compiler (dmd) gives
> me the following error:
> 
> source\main.d(24): Error: template plattform.profile_scope cannot deduce
> function from argument types !()(string), candidates are:
> source\plattform.d(262):        plattform.profile_scope(string name)
> 
> This looks like a deduction/overload kind of error, which has me completly confused since there is no other identifier of the name "profile_scope" in my programm and the error message shows only one candidate.
> 
> Is there any way I can achive generating those two statements using only something that effectively looks like a function call/C-macro with an argument?
The error you are seeing is due to the fact that you pass "func1" as
runtime parameter instead of as a teamplate parameter. You would need to
do `profile_scope!("func1");`. However, this will still not work and
you'll get an error message similar to this:
```
main.d(4): Error: incomplete mixin expression writeln("func1");
scope(exit) writeln("func1");
main.d(8): Error: template instance `main.profile_scope!"func1"` error
instantiating
```

The underlying problem is that you can only alias expressions, i.e. you cannot use alias to generate macro like functionality.

`measure("func1"); scope(exit) measure("func1");` is not an expression,
it is simply a statement, so the alias cannot work.

If you want to inject code directly at some place, basically your only
option is to use a string mixin. I understand that you do not want to
write `mixin` all the time, but to me, this does not look that bad:
```
auto profile_scope(string name)
{
    import std.format : format;
    return q{import std.stdio : writeln; writeln("%1$s"); scope(exit)
writeln("%1$s");}.format(name);
}

void main()
{
    mixin(profile_scope("func1"));
}
```
(replace writeln with your appropriate function call, "measure" in your
example).

March 07, 2019
On Thursday, 7 March 2019 at 20:34:48 UTC, Johannes Loher wrote:

> auto profile_scope(string name)
> {
>     import std.format : format;
>     return q{import std.stdio : writeln; writeln("%1$s"); scope(exit)
> writeln("%1$s");}.format(name);
> }
>
> void main()
> {
>     mixin(profile_scope("func1"));
> }

Is there a way to achieve this while compiling with -betterC? I use a custom string struct right now, and your version needs TypeInfo, concatening using ~ needs the garbage collector. I have the feeling D is really not agreeing with the way I want to do things. If this is not possible, I will just roll with the Adam's struct solution.

March 07, 2019
Am 07.03.19 um 22:21 schrieb Simon:
> 
> Is there a way to achieve this while compiling with -betterC? I use a custom string struct right now, and your version needs TypeInfo, concatening using ~ needs the garbage collector. I have the feeling D is really not agreeing with the way I want to do things. If this is not possible, I will just roll with the Adam's struct solution.
> 

Using Adams struct solution is perfectly fine. I believe it is probably the cleaner solution overall.

My solution depends on TypeInfo because format does. I used format because it makes the string more readable. You can avoid this and use string concatenation in combination with providing the function name as template parameter instead:

```
enum profile_scope(string name) = "import core.stdc.stdio : printf;
printf(\""
    ~ name ~ "\n\"); scope(exit) printf(\"" ~ name ~ "\n\");";

extern (C) void main()
{
    mixin(profile_scope!"func1");
}

```
This uses string concatenation only at compile time and not during run
time, so it does not require the garbage collector and is compatible
with betterC :)
March 07, 2019
Am 07.03.19 um 22:50 schrieb Johannes Loher:
> [...]

As a small addition, if you always want to pass the function name as a parameter, you can simplify this to the following:

```
enum profile_scope(string name = __FUNCTION__) = "import core.stdc.stdio
: printf; printf(\""
    ~ name ~ "\n\"); scope(exit) printf(\"" ~ name ~ "\n\");";

extern (C) void main()
{
    mixin(profile_scope!());
    foo();
}

extern (C) void foo() {
    mixin(profile_scope!());
}
```
This will print
```
main.main
main.foo
main.foo
main.main
```
to the commandline.

March 08, 2019
On Thursday, 7 March 2019 at 21:50:17 UTC, Johannes Loher wrote:
> ```
> enum profile_scope(string name) = "import core.stdc.stdio : printf;
> printf(\""
>     ~ name ~ "\n\"); scope(exit) printf(\"" ~ name ~ "\n\");";
>
> extern (C) void main()
> {
>     mixin(profile_scope!"func1");
> }
>
> ```
> This uses string concatenation only at compile time and not during run
> time, so it does not require the garbage collector and is compatible
> with betterC :)

Thanks, this works flawlessly. Out of interest: what is the "enum" doing there? I had the exact same behaviour in a function before, that I only called at compile-time, so why did it complain then? Can I somehow tell the compiler that a function should only be available at compile-time?
March 09, 2019
On Friday, 8 March 2019 at 11:42:11 UTC, Simon wrote:
> Thanks, this works flawlessly. Out of interest: what is the "enum" doing there? I had the exact same behaviour in a function before, that I only called at compile-time, so why did it complain then? Can I somehow tell the compiler that a function should only be available at compile-time?

The enum (which in D is not only for enumerations, but also for "manifest constants") ensures it is evaluated at compile-time, since things are only evaluated at compile-time if they have to.
See also Adam D. Ruppe's post in this thread:
https://forum.dlang.org/post/blaawtdhljjantvgafud@forum.dlang.org
March 10, 2019
On Saturday, 9 March 2019 at 09:12:13 UTC, Dennis wrote:
> On Friday, 8 March 2019 at 11:42:11 UTC, Simon wrote:
>> Thanks, this works flawlessly. Out of interest: what is the "enum" doing there? I had the exact same behaviour in a function before, that I only called at compile-time, so why did it complain then? Can I somehow tell the compiler that a function should only be available at compile-time?
>
> The enum (which in D is not only for enumerations, but also for "manifest constants") ensures it is evaluated at compile-time, since things are only evaluated at compile-time if they have to.
> See also Adam D. Ruppe's post in this thread:
> https://forum.dlang.org/post/blaawtdhljjantvgafud@forum.dlang.org

Thanks!