www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Variable arguments with file and line information?

reply "Namespace" <rswhite4 googlemail.com> writes:
Hi.
Is it possible to write something like that?
----
void error(Args...)(string msg, Args args, string file = 
__FILE__, size_t line = __LINE__) { ... }
----
?
Currently not, but how could it be done? I wont like to write:
----
error(format(msg, args));
----

Thanks in advance. :)
Nov 16 2013
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Saturday, 16 November 2013 at 22:57:35 UTC, Namespace wrote:
 Hi.
 Is it possible to write something like that?
 ----
 void error(Args...)(string msg, Args args, string file = 
 __FILE__, size_t line = __LINE__) { ... }
 ----
 ?
 Currently not, but how could it be done? I wont like to write:
 ----
 error(format(msg, args));
 ----

 Thanks in advance. :)
It is always surprising how quickly one has found its own solution, after you have posted here... :) ---- import std.stdio; import std.string : format; template error(string file = __FILE__, size_t line = __LINE__, Args...) { void error(string msg, Args args) { static if (args.length != 0) msg = .format(msg, args); writeln(.format(msg ~ ". In file %s on line %d.", file, line)); } } void main() { error("hallo"); error("Hallo %s.", "du da"); } ----
Nov 16 2013
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, November 17, 2013 00:09:53 Namespace wrote:
 On Saturday, 16 November 2013 at 22:57:35 UTC, Namespace wrote:
 Hi.
 Is it possible to write something like that?
 ----
 void error(Args...)(string msg, Args args, string file =
 __FILE__, size_t line = __LINE__) { ... }
 ----
 ?
 Currently not, but how could it be done? I wont like to write:
 ----
 error(format(msg, args));
 ----
 
 Thanks in advance. :)
It is always surprising how quickly one has found its own solution, after you have posted here... :) ---- import std.stdio; import std.string : format; template error(string file = __FILE__, size_t line = __LINE__, Args...) { void error(string msg, Args args) { static if (args.length != 0) msg = .format(msg, args); writeln(.format(msg ~ ". In file %s on line %d.", file, line)); } } void main() { error("hallo"); error("Hallo %s.", "du da"); } ----
If you're dealing with variadic arguments, then making the file and line number be template arguments is really your only solution. However, I must warn you that that will result in a new template instantation _every_ time that you use error, because the file and line number are always going to be different unless you call the function multiple times on the same line). So, this approach is pretty much guaranteed to generate template bloat. That may be acceptable, but I'd personally suggest trying to find a different way to go about solving the problem unless error is not going to be called very often - e.g. force the caller to call format when creating the message rather than supporting variadic arguments directly in error. - Jonathan M Davis
Nov 16 2013
next sibling parent reply "Chris Nicholson-Sauls" <ibisbasenji gmail.com> writes:
On Saturday, 16 November 2013 at 23:55:47 UTC, Jonathan M Davis 
wrote:
 If you're dealing with variadic arguments, then making the file 
 and line number
 be template arguments is really your only solution. However, I 
 must warn you
 that that will result in a new template instantation _every_ 
 time that you use
 error, because the file and line number are always going to be 
 different unless
 you call the function multiple times on the same line). So, 
 this approach is
 pretty much guaranteed to generate template bloat. That may be 
 acceptable, but
 I'd personally suggest trying to find a different way to go 
 about solving the
 problem unless error is not going to be called very often - 
 e.g. force the
 caller to call format when creating the message rather than 
 supporting
 variadic arguments directly in error.

 - Jonathan M Davis
Something I'm wondering: if one were to split the implementation along these lines: void error (string file = __FILE__, size_t line = __LINE__, Args...) (string msg, Args args) { errorImpl(file, line, msg, args); } void errorImpl (Args...) (string file, size_t line, string msg, Args args) {...} What are the chances of the middle-man function being inlined, thus cutting down on template bloat in the final product? I should hope it would be practically guaranteed.
Nov 17 2013
next sibling parent "Dicebot" <public dicebot.lv> writes:
 What are the chances of the middle-man function being inlined, 
 thus cutting down on template bloat in the final product?  I 
 should hope it would be practically guaranteed.
Even full inlining can't and won't do anything about template bloat within D semantics. This approach will, however, make size of bloated instances neglectible effective negating much of the problem in stripped binary.
Nov 17 2013
prev sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, November 17, 2013 13:06:11 Chris Nicholson-Sauls wrote:
 Something I'm wondering: if one were to split the implementation
 along these lines:
 
 
 void error (string file = __FILE__, size_t line = __LINE__,
 Args...) (string msg, Args args) {
      errorImpl(file, line, msg, args);
 }
 
 void errorImpl (Args...) (string file, size_t line, string msg,
 Args args) {...}
 
 
 What are the chances of the middle-man function being inlined,
 thus cutting down on template bloat in the final product?  I
 should hope it would be practically guaranteed.
Without using -inline, the odds are zero. With the -inline... maybe. To be honest, dmd's inliner is horrible. e.g. it completely fails to inline any of std.ascii right now even though almost everything in there is a one line function. As I understand it, the fact that error and errorImpl are templates make it much more likely that they'll be inlined, but you'd have to compile them with -inline and look at the generated assembly code to find out for sure. We really, really need to improve the inliner. But regardless, inlining has no effect on "template bloat." The fact that the function was inline does not get rid of the fact that it was instantiated, and it's possible that it would be inlined in some circumstances and not in others, making it so that the function definition would have to stick around. Also, I don't think that dmd does a very good job of stripping out template instantiations that aren't needed anymore (e.g. the ones used in template constraints like isForwardRange or isSomeString), so it _definitely_ isn't going to be smart enough to remove an instantiated template that's been inlined and then not used somewhere where it wasn't inlined. - Jonathan M Davis
Nov 17 2013
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
17-Nov-2013 16:36, Jonathan M Davis пишет:
 On Sunday, November 17, 2013 13:06:11 Chris Nicholson-Sauls wrote:
 Something I'm wondering: if one were to split the implementation
 along these lines:


 void error (string file = __FILE__, size_t line = __LINE__,
 Args...) (string msg, Args args) {
       errorImpl(file, line, msg, args);
 }

 void errorImpl (Args...) (string file, size_t line, string msg,
 Args args) {...}


 What are the chances of the middle-man function being inlined,
 thus cutting down on template bloat in the final product?  I
 should hope it would be practically guaranteed.
Without using -inline, the odds are zero. With the -inline... maybe. To be honest, dmd's inliner is horrible. e.g. it completely fails to inline any of std.ascii right now even though almost everything in there is a one line function.
At least we should fix this embarrassing limitation that kills inlining of std.ascii on ALL compilers. https://d.puremagic.com/issues/show_bug.cgi?id=10985 And it's not only std.ascii pretty much all non-templated stuff.
 We really, really need to improve the inliner.
Amen. -- Dmitry Olshansky
Nov 17 2013
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, November 17, 2013 16:49:11 Dmitry Olshansky wrote:
 At least we should fix this embarrassing limitation that kills  inlining
 of std.ascii on ALL compilers.
 
 https://d.puremagic.com/issues/show_bug.cgi?id=10985
 And it's not only std.ascii pretty much all non-templated stuff.
Yeah, std.ascii is just a concrete example that I'm aware of. - Jonathan M Davis
Nov 17 2013
prev sibling parent reply "Rob T" <alanb ucora.com> writes:
On Saturday, 16 November 2013 at 23:55:47 UTC, Jonathan M Davis 
wrote:
[...]
 e.g. force the
 caller to call format when creating the message rather than 
 supporting
 variadic arguments directly in error.

 - Jonathan M Davis
OK, how about this implementation? string msg( S : string, T... )( S a_Msg, T a_Args ) { if ( a_Args.length != 0 ) a_Msg = std.string.format(a_Msg, a_Args); return a_Msg; } void error(lazy string a_Msg, string file = __FILE__, size_t line = __LINE__) { auto v_Msg = a_Msg(); writeln(.format(v_Msg ~ ". In file %s on line %d.", file, line)); } void main() { error(msg("hallo") ); error(msg("Hallo %s.", "du da") ); // I think this looks a whole lot better msg("hallo").error; msg("Hallo %s.", "du da").error; } It works, but still would be convenient if there was another more simpler way of getting the job done, also if it were more obvious. Once it's done though, it's rather nice I think. --rt
Nov 17 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, November 17, 2013 20:11:20 Rob T wrote:
 On Saturday, 16 November 2013 at 23:55:47 UTC, Jonathan M Davis
 wrote:
 [...]
 
 e.g. force the
 caller to call format when creating the message rather than
 supporting
 variadic arguments directly in error.
 
 - Jonathan M Davis
OK, how about this implementation? string msg( S : string, T... )( S a_Msg, T a_Args ) { if ( a_Args.length != 0 ) a_Msg = std.string.format(a_Msg, a_Args); return a_Msg; }
Why bother with this? Just use format directly. As written, msg is a useless wrapper function for format.
 void error(lazy string a_Msg, string file = __FILE__, size_t line
 = __LINE__)
 {
      auto v_Msg = a_Msg();
      writeln(.format(v_Msg ~ ". In file %s on line %d.", file,
 line));
 }
And as error is currently written, making the message lazy is pointless and increases overhead. lazy is only valuable if that parameter might not be used (e.g with the message to enforce). Also, if you're going to use a format string, you should use writefln rather than calling format and passing the result to writeln. It's almost certainly less overhead to call writefln.
 void main()
 {
     error(msg("hallo") );
     error(msg("Hallo %s.", "du da") );
 
     // I think this looks a whole lot better
     msg("hallo").error;
     msg("Hallo %s.", "du da").error;
 
 }
 
 It works, but still would be convenient if there was another more
 simpler way of getting the job done, also if it were more
 obvious. Once it's done though, it's rather nice I think.
It would be nice if we could get __FILE__ and __LINE__ to work as function arguments to variadic functions, but for now, that just isn't possible. - Jonathan M Davis
Nov 17 2013
parent reply "Rob T" <alanb ucora.com> writes:
Good points, got it down to this.

void error(string a_Msg, string file = __FILE__, size_t line = 
__LINE__)
{
    writefln( a_Msg ~ ". In file %s on line %d.", file, line );
}

int main()
{
    format("hallo").error;
    format("Hallo %s.", "du da").error;
}

There should be no more template bloat, and it looks to be about 
as usable and simple as possible.

Do you see any further optimizations that do not increase the 
usage convenience?

--rt
Nov 17 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, November 17, 2013 22:08:38 Rob T wrote:
 Good points, got it down to this.
 
 void error(string a_Msg, string file = __FILE__, size_t line =
 __LINE__)
 {
     writefln( a_Msg ~ ". In file %s on line %d.", file, line );
 }
 
 int main()
 {
     format("hallo").error;
     format("Hallo %s.", "du da").error;
 }
 
 There should be no more template bloat, and it looks to be about
 as usable and simple as possible.
 
 Do you see any further optimizations that do not increase the
 usage convenience?
Yeah. Don't use concatenation in your format string: writefln("%s. In file %s on line %s.", msg, file, line); Other than that, it looks fine. Personally, I'd change it to writefln("%s(%s): %s", file, line, msg); but that's personal preference. - Jonathan M Davis
Nov 17 2013
parent "Rob T" <alanb ucora.com> writes:
On Sunday, 17 November 2013 at 21:29:03 UTC, Jonathan M Davis 
wrote:
 Yeah. Don't use concatenation in your format string:
Right, that concat was bothering me too. Tks for the input. --rt
Nov 17 2013
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Namespace:

 It is always surprising how quickly one has found its own 
 solution, after you have posted here... :)
This is a well known psychological phenomenon: when you explain your problem to other people you lay down the problem very well, its constraints, its invariants, your needs, and this helps a lot the problem-solving parts of your mind find a solution :-) This phenomenon is so strong, that some researchers assume to know a topic well enough only after they have taught one class about it :-) That's why keeping a "laboratory notebook" for your programming activities, where you write down and explain what you do and what you have done, helps solve your problems. Unfortunately many computer science teachers don't explain their students why and how to keep such notebook. Bye, bearophile
Nov 16 2013
prev sibling parent Timothee Cour <thelastmammoth gmail.com> writes:
unfortunately this problem keeps arising every so often and the only thing
we have are workarounds

to name a few:
digitalmars.D - Typesafe variadics in any position
feature request: special optional argument (__FILE__, ...) AFTER variadic
template


On Sat, Nov 16, 2013 at 3:55 PM, Jonathan M Davis <jmdavisProg gmx.com>wrote:

 On Sunday, November 17, 2013 00:09:53 Namespace wrote:
 On Saturday, 16 November 2013 at 22:57:35 UTC, Namespace wrote:
 Hi.
 Is it possible to write something like that?
 ----
 void error(Args...)(string msg, Args args, string file =
 __FILE__, size_t line = __LINE__) { ... }
 ----
 ?
 Currently not, but how could it be done? I wont like to write:
 ----
 error(format(msg, args));
 ----

 Thanks in advance. :)
It is always surprising how quickly one has found its own solution, after you have posted here... :) ---- import std.stdio; import std.string : format; template error(string file = __FILE__, size_t line = __LINE__, Args...) { void error(string msg, Args args) { static if (args.length != 0) msg = .format(msg, args); writeln(.format(msg ~ ". In file %s on line %d.", file,
line));
       }
 }

 void main()
 {
       error("hallo");
       error("Hallo %s.", "du da");
 }
 ----
If you're dealing with variadic arguments, then making the file and line number be template arguments is really your only solution. However, I must warn you that that will result in a new template instantation _every_ time that you use error, because the file and line number are always going to be different unless you call the function multiple times on the same line). So, this approach is pretty much guaranteed to generate template bloat. That may be acceptable, but I'd personally suggest trying to find a different way to go about solving the problem unless error is not going to be called very often - e.g. force the caller to call format when creating the message rather than supporting variadic arguments directly in error. - Jonathan M Davis
Nov 16 2013