digitalmars.D.learn - Throw an exception but "hide" the top frame?
- aldanor (38/38) Dec 24 2014 Imagine there's a template that wraps arbitrary functions and may
- ketmar via Digitalmars-d-learn (12/61) Dec 24 2014 On Wed, 24 Dec 2014 13:38:59 +0000
- aldanor (21/33) Dec 24 2014 Hmm, that makes sense. After some pondering, I think I've hacked
- ketmar via Digitalmars-d-learn (6/48) Dec 24 2014 On Wed, 24 Dec 2014 14:04:50 +0000
- ketmar via Digitalmars-d-learn (1/1) Dec 24 2014 "has to generate new parameterized function", of course. sorry.
- Adam D. Ruppe (4/6) Dec 24 2014 You can just do regular params (most the time), don't have to be
- Adam D. Ruppe (12/16) Dec 24 2014 Since this is just passed as arguments to the constructor, you
- aldanor (17/22) Dec 24 2014 Thanks! I guess that's what my confusion was partially about --
- Sean Kelly (4/4) Dec 24 2014 The backtrace code has a parameter that lets you tell it how many
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
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] =20the `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
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
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:template bloat. now compiler can't use one instance of parameterized template function for each call, but has to generate new template each time.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)?
Dec 24 2014
"has to generate new parameterized function", of course. sorry.
Dec 24 2014
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
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
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); // L10Thanks! 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
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