www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Throw an exception but "hide" the top frame?

reply "aldanor" <i.s.smirnov gmail.com> writes:
Imagine there's a template that wraps arbitrary functions and may 
throw exceptions depending on their returned values (see a 
simplified example below). However, if an exception occurs, the 
backtrace is pointing inside the template which is not helpful at 
all (especially when many such functions have been wrapped). Is 
it somehow possible to throw an exception "from the parent frame" 
so the backtrace would never drop into the template body?

Here's an example:

     module wrap;

     import std.stdio;
     import std.string;

     auto check(alias func)(int x) {
         auto result = func(x);
         if (result < 0) // throw on negative return value
             throw new Exception("%d < 0".format(result)); // L10
         return x; // otherwise, pass the result through
     }

     int f(int x) {
         return !(x % 2) ? x : -x;
     }

     void main() {
         import std.stdio;
         alias g = check!f;
         writeln(g(2)); // ok
         writeln(g(3)); // should fail // L21
     }

which prints something lke this when run:

     2
     object.Exception wrap.d(10): -3 < 0
     ----------------
     .. (int wrap.check!(_D4wrap1fFiZi).check(int)+0x13) [0x44cf87]
     .. (_Dmain+0x20) [0x448da4]

Is it possible to throw an exception in a way that backtrace 
would be more like this?

     2
     object.Exception wrap.d(21): -3 < 0
     ----------------
     .. (_Dmain+0x20) [0x448da4]
Dec 24 2014
next sibling parent reply ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Wed, 24 Dec 2014 13:38:59 +0000
aldanor via Digitalmars-d-learn <digitalmars-d-learn puremagic.com>
wrote:

 Imagine there's a template that wraps arbitrary functions and may=20
 throw exceptions depending on their returned values (see a=20
 simplified example below). However, if an exception occurs, the=20
 backtrace is pointing inside the template which is not helpful at=20
 all (especially when many such functions have been wrapped). Is=20
 it somehow possible to throw an exception "from the parent frame"=20
 so the backtrace would never drop into the template body?
=20
 Here's an example:
=20
      module wrap;
=20
      import std.stdio;
      import std.string;
=20
      auto check(alias func)(int x) {
          auto result =3D func(x);
          if (result < 0) // throw on negative return value
              throw new Exception("%d < 0".format(result)); // L10
          return x; // otherwise, pass the result through
      }
=20
      int f(int x) {
          return !(x % 2) ? x : -x;
      }
=20
      void main() {
          import std.stdio;
          alias g =3D check!f;
          writeln(g(2)); // ok
          writeln(g(3)); // should fail // L21
      }
=20
 which prints something lke this when run:
=20
      2
      object.Exception wrap.d(10): -3 < 0
      ----------------
      .. (int wrap.check!(_D4wrap1fFiZi).check(int)+0x13) [0x44cf87]
      .. (_Dmain+0x20) [0x448da4]
=20
 Is it possible to throw an exception in a way that backtrace=20
 would be more like this?
=20
      2
      object.Exception wrap.d(21): -3 < 0
      ----------------
      .. (_Dmain+0x20) [0x448da4]
=20
the `object.Exception wrap.d` is not a backtrace result, this is the result of Exception class constructor: this (string msg, string file=3D__FILE__, usize line=3D__LINE__, Throwabl= e next=3Dnull) it registering the file and line of exception object creation in COMPILE time. so you can do nothing with it, as there is no way to know what called what in compile time. p.s. if you can use GDC, for example, and turn on debug info, backtrace will show you files and line numbers for every address.
Dec 24 2014
parent reply "aldanor" <i.s.smirnov gmail.com> writes:
On Wednesday, 24 December 2014 at 13:48:26 UTC, ketmar via 
Digitalmars-d-learn wrote:
 the `object.Exception wrap.d` is not a backtrace result, this 
 is the
 result of Exception class constructor:

   this (string msg, string file=__FILE__, usize line=__LINE__, 
 Throwable next=null)

 it registering the file and line of exception object creation in
 COMPILE time. so you can do nothing with it, as there is no way 
 to know
 what called what in compile time.

 p.s. if you can use GDC, for example, and turn on debug info,
 backtrace will show you files and line numbers for every 
 address.
Hmm, that makes sense. After some pondering, I think I've hacked together a workaround though: template check(alias func) { auto check(string file = __FILE__, int line = __LINE__)(int x) { auto result = func(x); if (result < 0) // throw on negative return vaule throw new Exception("%d < 0".format(result), file, line); return x; // otherwise, pass the result through } } which produces: 2 object.Exception wrap.d(23): -3 < 0 ---------------- .. (_Dmain+0x20) [0x448de4] Are there any hidden downsides to doing it this way (aside from generating additional code on every call)?
Dec 24 2014
next sibling parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Wed, 24 Dec 2014 14:04:50 +0000
aldanor via Digitalmars-d-learn <digitalmars-d-learn puremagic.com>
wrote:

 On Wednesday, 24 December 2014 at 13:48:26 UTC, ketmar via=20
 Digitalmars-d-learn wrote:
 the `object.Exception wrap.d` is not a backtrace result, this=20
 is the
 result of Exception class constructor:

   this (string msg, string file=3D__FILE__, usize line=3D__LINE__,=20
 Throwable next=3Dnull)

 it registering the file and line of exception object creation in
 COMPILE time. so you can do nothing with it, as there is no way=20
 to know
 what called what in compile time.

 p.s. if you can use GDC, for example, and turn on debug info,
 backtrace will show you files and line numbers for every=20
 address.
=20 Hmm, that makes sense. After some pondering, I think I've hacked=20 together a workaround though: =20 template check(alias func) { auto check(string file =3D __FILE__, int line =3D=20 __LINE__)(int x) { auto result =3D func(x); if (result < 0) // throw on negative return vaule throw new Exception("%d < 0".format(result),=20 file, line); return x; // otherwise, pass the result through } } =20 which produces: =20 2 object.Exception wrap.d(23): -3 < 0 ---------------- .. (_Dmain+0x20) [0x448de4] =20 =20 Are there any hidden downsides to doing it this way (aside from=20 generating additional code on every call)?
template bloat. now compiler can't use one instance of parameterized template function for each call, but has to generate new template each time.
Dec 24 2014
prev sibling next sibling parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
"has to generate new parameterized function", of course. sorry.
Dec 24 2014
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 24 December 2014 at 14:04:51 UTC, aldanor wrote:
         auto check(string file = __FILE__, int line = 
 __LINE__)(int x) {
You can just do regular params (most the time), don't have to be template params. My last email shows that too. Then it doesn't make new templates on each use.
Dec 24 2014
prev sibling next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 24 December 2014 at 13:39:00 UTC, aldanor wrote:
 (especially when many such functions have been wrapped). Is it 
 somehow possible to throw an exception "from the parent frame" 
 so the backtrace would never drop into the template body?
Since this is just passed as arguments to the constructor, you can ask for the same argument in your function and forward them.
     auto check(alias func)(int x) {
Like make that auto check(alias func)(int x, string file = __FILE__, size_t line = __LINE__) { /* snip */ throw new Exception("%d < 0".format(result), file, line); // L10 The default values are auto-filled by the compiler to be the call site, then you pass it on to Exception so when it prints, it shows that,
Dec 24 2014
parent "aldanor" <i.s.smirnov gmail.com> writes:
On Wednesday, 24 December 2014 at 14:11:37 UTC, Adam D. Ruppe 
wrote:
 auto check(alias func)(int x, string file = __FILE__, size_t 
 line = __LINE__) {
     /* snip */
        throw new Exception("%d < 0".format(result), file, 
 line); // L10
Thanks! I guess that's what my confusion was partially about -- when exactly the __FILE__ and __LINE__ are substituted (particularly in the case when you're dealing with templates). It looks like this would even work with generic callables: auto check(alias func)(ParameterTypeTuple!(func) args, string __line__ = __LINE__, int __file__ == __FILE__) { static if (is(ReturnType!(func) == void)) func(args); else { auto result = func(args); if (result < 0) throw new Exception("foo", __line__, __file__); return result; } }
Dec 24 2014
prev sibling parent "Sean Kelly" <sean invisibleduck.org> writes:
The backtrace code has a parameter that lets you tell it how many 
leading frames you want it to skip when generating the result.  
This is to get out of the Throwable ctor code itself, but it 
wouldn't be hard to bump this by one or two if you need it to.
Dec 24 2014