www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Aliasing a mixin (or alternative ways to profile a scope)

reply Simon <simon.vanbernem yahoo.de> writes:
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?
Mar 07
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
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.
Mar 07
prev sibling parent reply Johannes Loher <johannesloher fg4f.de> writes:
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).
Mar 07
parent reply Simon <simon.vanbernem yahoo.de> writes:
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.
Mar 07
parent reply Johannes Loher <johannesloher fg4f.de> writes:
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 :)
Mar 07
next sibling parent Johannes Loher <johannesloher fg4f.de> writes:
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.
Mar 07
prev sibling parent reply Simon <simon.vanbernem yahoo.de> writes:
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?
Mar 08
parent reply Dennis <dkorpel gmail.com> writes:
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
Mar 09
parent Simon <simon.vanbernem yahoo.de> writes:
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!
Mar 10