www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - hidden passing of __FILE__ and __LINE__ into function

reply Dmitry <dmitry indiedev.ru> writes:
Hi there.

Currently for messages about errors I use code like this:

     void add(string name, ref Scene scene)
     {
         if (name in listOfScenes)
         {
             EError(__FILE__, __LINE__, "scene already exists".L, 
quoted(name));
         }
     ...
     }

Is there way for avoid using (avoid writing) `__FILE__` and 
`__LINE__` in each call? I.e. I want use simple

     EError("scene already exists".L, quoted(name));

but function `EError` must print info (file and line) of where 
was called.

P.S. `EError` just prints info into console, result for this 
example is:

 [Error] (source\core\EScene.d, 35) Scene already exists: "Scene 
 1"
and code is: void EError(S...)(S args) { write("[Error] (", args[0], ", ", args[1], ") ", args[2..$], '\n');} }
Apr 17
parent reply Basile B. <b2.temp gmx.com> writes:
On Monday, 17 April 2017 at 10:22:47 UTC, Dmitry wrote:
 Hi there.

 Currently for messages about errors I use code like this:

     void add(string name, ref Scene scene)
     {
         if (name in listOfScenes)
         {
             EError(__FILE__, __LINE__, "scene already 
 exists".L, quoted(name));
         }
     ...
     }

 Is there way for avoid using (avoid writing) `__FILE__` and 
 `__LINE__` in each call? I.e. I want use simple

     EError("scene already exists".L, quoted(name));

 but function `EError` must print info (file and line) of where 
 was called.

 P.S. `EError` just prints info into console, result for this 
 example is:

 [Error] (source\core\EScene.d, 35) Scene already exists: 
 "Scene 1"
and code is: void EError(S...)(S args) { write("[Error] (", args[0], ", ", args[1], ") ", args[2..$], '\n');} }
when used as template value parameter, __FILE__ and __LINE__evaluate to the file and line of the call site. So help yourself ans use something like this: void error(string f = __FILE__, int l = __LINE__)(string msg){}
Apr 17
next sibling parent Dmitry <dmitry indiedev.ru> writes:
On Monday, 17 April 2017 at 10:30:35 UTC, Basile B. wrote:
 when used as template value parameter, __FILE__ and 
 __LINE__evaluate to the file and line of the call site. So help 
 yourself ans use something like this:

 void error(string f = __FILE__, int l = __LINE__)(string msg){}
Thank you, Basile, it works perfect.
Apr 17
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, April 17, 2017 10:30:35 Basile B. via Digitalmars-d-learn wrote:
 On Monday, 17 April 2017 at 10:22:47 UTC, Dmitry wrote:
 Hi there.

 Currently for messages about errors I use code like this:
     void add(string name, ref Scene scene)
     {

         if (name in listOfScenes)
         {

             EError(__FILE__, __LINE__, "scene already

 exists".L, quoted(name));

         }

     ...
     }

 Is there way for avoid using (avoid writing) `__FILE__` and
 `__LINE__` in each call? I.e. I want use simple

     EError("scene already exists".L, quoted(name));

 but function `EError` must print info (file and line) of where
 was called.

 P.S. `EError` just prints info into console, result for this

 example is:
 [Error] (source\core\EScene.d, 35) Scene already exists:
 "Scene 1"
and code is: void EError(S...)(S args) { write("[Error] (", args[0], ", ", args[1], ") ", args[2..$], '\n');} }
when used as template value parameter, __FILE__ and __LINE__evaluate to the file and line of the call site. So help yourself ans use something like this: void error(string f = __FILE__, int l = __LINE__)(string msg){}
They works, but it results in a new template being instantiated for every call, so you really shouldn't use __FILE__ or __LINE__ as template arguments if you can avoid it. Usually, the better way to handle it is to use runtime arguments, e.g. void error(string msg, string file = __FILE__, size_t line = __LINE__) { ... } That's what Exception's constructor does as well as functions like std.exception.enforce. - Jonathan M Davis
Apr 17
next sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Monday, 17 April 2017 at 10:55:30 UTC, Jonathan M Davis wrote:
 On Monday, April 17, 2017 10:30:35 Basile B. via 
 Digitalmars-d-learn wrote:
 On Monday, 17 April 2017 at 10:22:47 UTC, Dmitry wrote:
void error(string msg, string file = __FILE__, size_t line = __LINE__) { ... } That's what Exception's constructor does as well as functions like std.exception.enforce. - Jonathan M Davis
I didn't know that this also works for regular parameters. Then it's obviously better.
Apr 17
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, April 17, 2017 10:58:36 Basile B. via Digitalmars-d-learn wrote:
 On Monday, 17 April 2017 at 10:55:30 UTC, Jonathan M Davis wrote:
 On Monday, April 17, 2017 10:30:35 Basile B. via

 Digitalmars-d-learn wrote:
 On Monday, 17 April 2017 at 10:22:47 UTC, Dmitry wrote:
void error(string msg, string file = __FILE__, size_t line = __LINE__) { ... } That's what Exception's constructor does as well as functions like std.exception.enforce. - Jonathan M Davis
I didn't know that this also works for regular parameters. Then it's obviously better.
It doesn't in C++ (which is _really_ annoying), but it does in D. In C++, it uses the file and line number of the declaration site, whereas in D, it uses the file and line number of the call site. - Jonathan M Davis
Apr 17
prev sibling parent reply Dmitry <dmitry indiedev.ru> writes:
On Monday, 17 April 2017 at 10:55:30 UTC, Jonathan M Davis wrote:
 They works, but it results in a new template being instantiated 
 for every call, so you really shouldn't use __FILE__ or 
 __LINE__ as template arguments if you can avoid it.
Does it matter if I anyway use template (S...) ? And what problem with that new templates for every call? Increases .exe size? Needs more memory (runtime? compile-time?)? Something else?
 Usually, the better way to handle it is to use runtime 
 arguments, e.g.
 void error(string msg, string file = __FILE__, size_t line = 
 __LINE__)
Is possible use this with (S...)? In some cases I use many arguments (5-10, mixed strings and numbers) and I tried to avoid the concatenation them into string. What will be better? Concatenation or templates? Or maybe an another way?
Apr 17
next sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, April 17, 2017 13:45:18 Dmitry via Digitalmars-d-learn wrote:
 On Monday, 17 April 2017 at 10:55:30 UTC, Jonathan M Davis wrote:
 They works, but it results in a new template being instantiated
 for every call, so you really shouldn't use __FILE__ or
 __LINE__ as template arguments if you can avoid it.
Does it matter if I anyway use template (S...) ? And what problem with that new templates for every call? Increases .exe size? Needs more memory (runtime? compile-time?)? Something else?
Every time there's a new template instantiation, it's essentially copy-pasting the entire template. So, if you have the templated function auto foo(T)(T bar) { return bar; } and then call foo(5); foo(true); foo("hello"); then you get the equivalent of int foo!int(int bar) { return bar; } bool foo!bool(bool bar) { return bar; } string foo!string(string bar) { return bar; } in your program. If you have string foo(string file = __FILE__, size_t line = line)(string bar) { return bar; } and you call that function 17 times, then you get 17 separate functions. If you call it 120 times you get 120 separate functions. So, if you call the function very many times, the template bloat would be enormous. The executable will be _much_ larger and will thus take up much more space on disk and much more RAM when it's loaded into memory. In some cases, that makes sense, but it usually doesn't.
 Usually, the better way to handle it is to use runtime
 arguments, e.g.
 void error(string msg, string file = __FILE__, size_t line =
 __LINE__)
Is possible use this with (S...)? In some cases I use many arguments (5-10, mixed strings and numbers) and I tried to avoid the concatenation them into string. What will be better? Concatenation or templates? Or maybe an another way?
The short answer is that if you're using variadic templates, you can't use default arguments. Something like auto foo(Args...)(Args args, string file = __FILE__, size_t line = __LINE__) { ... } auto result = foo("hello", 42); does not work. So, if you want to have the file and line number passed automatically with a variadic template, then you're forced to use template parameters instead of function paramaters and incur whatever bloat that goes with that. Now, that being said, surprisingly, it does look like it works to do auto foo(Args...)(Args args, string file = __FILE__, size_t line = __LINE__) { ... } auto result = foo!(string, int)("hello", 42); So, if you're okay with explicitly instantiating your variadic function template instead of having the types inferred, then it can work, but otherwise, no. Making it work would require a language enhancement, and even then, if you ever wanted to explicitly provide the file and line number arguments instead of using the default arguments, you'd almost certainly be forced to explicitly instantiate the template, since the compiler would have no other way of determining whether the file and line arguments on the end were intended to be the file and line arguments or just more variadic arguments. - Jonathan M Davis
Apr 17
next sibling parent Dmitry <dmitry indiedev.ru> writes:
On Monday, 17 April 2017 at 14:23:50 UTC, Jonathan M Davis wrote:
 So, if you're okay with explicitly instantiating your variadic 
 function template instead of having the types inferred, then it
Yes, it's my case. That's a game engine, so some kilobytes isn't a problem. Moreover, possible that function will be used only in debug mode. Thank you for explaining, I appreciate it.
Apr 17
prev sibling parent Lewis <musicaljelly gmail.com> writes:
On Monday, 17 April 2017 at 14:23:50 UTC, Jonathan M Davis wrote:
 On Monday, April 17, 2017 13:45:18 Dmitry via 
 Digitalmars-d-learn wrote:
 [...]
Every time there's a new template instantiation, it's essentially copy-pasting the entire template. So, if you have the templated function [...]
The other reason to avoid excessive template instantiation is that it inflates your compile time. A while back I changed my custom assertf() and logf() functions to be non-templated in exactly the way Jonathan M Davis described, and it shaved ~0.5s off my 3s build time. This particular project is currently only 8000 LOC, but on a larger project you can see the hit could be pretty significant.
Apr 18
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, April 17, 2017 07:23:50 Jonathan M Davis via Digitalmars-d-learn 
wrote:
 So, if you're okay with explicitly instantiating your variadic function
 template instead of having the types inferred, then it can work, but
 otherwise, no. Making it work would require a language enhancement
Actually, not only is there already a bug report for this https://issues.dlang.org/show_bug.cgi?id=8687 which is marked as a bug and not an enhancement, and when Walter commented on it when an ICE related to it was fixed, he didn't change it from a bug to an enhancement. So, it looks like he agrees that it's a bug rather than considering it an enhancement. It has yet to be fixed regardless though. - Jonathan M Davis
Apr 18
parent reply Solomon E <default avatar.org> writes:
On Tuesday, 18 April 2017 at 10:13:09 UTC, Jonathan M Davis wrote:
 On Monday, April 17, 2017 07:23:50 Jonathan M Davis via 
 Digitalmars-d-learn wrote:
 So, if you're okay with explicitly instantiating your variadic 
 function template instead of having the types inferred, then 
 it can work, but otherwise, no. Making it work would require a 
 language enhancement
Actually, not only is there already a bug report for this https://issues.dlang.org/show_bug.cgi?id=8687 which is marked as a bug and not an enhancement, and when Walter commented on it when an ICE related to it was fixed, he didn't change it from a bug to an enhancement. So, it looks like he agrees that it's a bug rather than considering it an enhancement. It has yet to be fixed regardless though. - Jonathan M Davis
import std.stdio: writeln; import std.conv: to; void main() { add("Scene 1", "Scene 2", "Scene 3"); add("Scene 3", "Scene 4", "Scene 5"); add(5, 6, 7); add(7, "Scene 8", "Scene 9"); writeln("total scenes added: ", sceneCount); } struct Scene { string name; string file; size_t line; } Scene*[] listOfScenes; int[string] indexOfScenes; int sceneCount = 0; void add_impl(T...)(string file, size_t line, T args) { foreach(arg; args) { static if (is(typeof(arg) == string)) { auto name = arg; } else { auto name = "Scene " ~ arg.to!string; } if (name in indexOfScenes) { EError(file, line, "scene already exists", name); } else { indexOfScenes[name] = sceneCount; listOfScenes ~= new Scene(name, file, line); sceneCount += 1; writeln("added " ~ name); } } } void add(string file = __FILE__, size_t line = __LINE__, T...)(T args) { add_impl!T(file, line, args); } void EError(string file, size_t line, string message, string name) { writeln(name, ": ", message, " in file ", file, " in line ", line); auto previous = listOfScenes[indexOfScenes[name]]; writeln("previous definition of ", previous.name, " was in ", previous.file, " in line ", previous.line); } /* output: added Scene 1 added Scene 2 added Scene 3 Scene 3: scene already exists in file vlf.d in line 7 previous definition of Scene 3 was in vlf.d in line 6 added Scene 4 added Scene 5 Scene 5: scene already exists in file vlf.d in line 8 previous definition of Scene 5 was in vlf.d in line 7 added Scene 6 added Scene 7 Scene 7: scene already exists in file vlf.d in line 9 previous definition of Scene 7 was in vlf.d in line 8 added Scene 8 added Scene 9 total scenes added: 9 */ I tried to produce an example of calling a function with variadic template arguments using special tokens __FILE__ and __LINE__. This compiles and runs, producing the output shown, using the default gdc provided by Ubuntu 17.04. This appears to be a workaround for Issue 8687. The instantiations of the wrapper function `add` should only add minimally to the compiled object code size, once per call of `add`. I'm not sure about the quality of any of this code.
Apr 18
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Tuesday, 18 April 2017 at 13:28:06 UTC, Solomon E wrote:

 I tried to produce an example of calling a function with
 variadic template arguments using special tokens __FILE__ and 
 __LINE__.

 This compiles and runs, producing the output shown, using the 
 default gdc
 provided by Ubuntu 17.04. This appears to be a workaround for 
 Issue 8687...
There's a much more concise workaround, both in code written and generated ;) import std.stdio; template func(string file = __FILE__, int line = __LINE__) { auto func(T...)(auto ref T args) { writeln("called func with ", T.length, " args at ", file, ":", line); } } void main() { func(); func(1, 2, 3); }
Apr 18
next sibling parent Solomon E <default avatar.org> writes:
On Tuesday, 18 April 2017 at 13:48:57 UTC, Stanislav Blinov wrote:
 On Tuesday, 18 April 2017 at 13:28:06 UTC, Solomon E wrote:

 I tried to produce an example of calling a function with
 variadic template arguments using special tokens __FILE__ and 
 __LINE__.

 This compiles and runs, producing the output shown, using the 
 default gdc
 provided by Ubuntu 17.04. This appears to be a workaround for 
 Issue 8687...
There's a much more concise workaround, both in code written and generated ;) import std.stdio; template func(string file = __FILE__, int line = __LINE__) { auto func(T...)(auto ref T args) { writeln("called func with ", T.length, " args at ", file, ":", line); } } void main() { func(); func(1, 2, 3); }
Thank you for reminding me that templates can contain a definition of a function of the same name, which still surprises me as convenient, not as a bad thing. Unfortunately, when I tried it, I found that while that attempt at a workaround is twice as concise in lines of code and symbols introduced, it is not acceptable. It gets the lines reported wrong. It reports the line for where the template was first instantiated for the same tuple of argument types, instead of exactly the line of each call of `func`.
Apr 18
prev sibling parent Dmitry <dmitry indiedev.ru> writes:
On Tuesday, 18 April 2017 at 13:48:57 UTC, Stanislav Blinov wrote:
 There's a much more concise workaround, both in code written 
 and generated ;)

 import std.stdio;

 template func(string file = __FILE__, int line = __LINE__)
 {
     auto func(T...)(auto ref T args)
     {
         writeln("called func with ", T.length, " args at ",
                 file, ":", line);
     }
 }

 void main()
 {
     func();
     func(1, 2, 3);
 }
Very nice. Thank you!
Apr 18