Thread overview
try & catch / repeating code - DRY
May 22, 2018
Robert M. Münch
May 22, 2018
Jacob Carlborg
May 24, 2018
Robert M. Münch
May 24, 2018
Jacob Carlborg
May 24, 2018
Jonathan M Davis
May 22, 2018
Ali Çehreli
May 23, 2018
Robert M. Münch
May 23, 2018
Ali Çehreli
May 23, 2018
Jonathan M Davis
May 23, 2018
Meta
May 22, 2018
I see that I'm writing

try {
... different code ...
} catch (myException e) {
	... same handling code ...
}

over and over again.

Of course I can put the exception handling code into a function to not duplicate it. However, I still need to write this construct over and over again. Is there a way to handle it more generic? Like:

??? (... code ...);

or

??? { ... code ...};

Where ??? would do the try and re-use the exception handling code everytime? I hope this is understandable.

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster

May 22, 2018
On 2018-05-22 20:20, Robert M. Münch wrote:
> I see that I'm writing
> 
> try {
> ... different code ...
> } catch (myException e) {
>      ... same handling code ...
> }
> 
> over and over again.
> 
> Of course I can put the exception handling code into a function to not duplicate it. However, I still need to write this construct over and over again. Is there a way to handle it more generic? Like:
> 
> ??? (... code ...);
> 
> or
> 
> ??? { ... code ...};
> 
> Where ??? would do the try and re-use the exception handling code everytime? I hope this is understandable.
> 

You can always create a function that takes a delegate or lambda and handles the exception in the function. Here are three versions of the same thing, depending on how you want the call site to look like.

void handleException1(alias dg)()
{
    try dg();
    catch (Exception e) { /* handle exception */ }
}

void handleException2(lazy void dg)
{
    try dg();
    catch (Exception e) { /* handle exception */ }
}

void handleException3(scope void delegate () dg)
{
    try dg();
    catch (Exception e) { /* handle exception */ }
}

void main()
{
    handleException1!({
        writeln("asd");
    });

    handleException1!(() => writeln("asd"));

    handleException2(writeln("asd"));

    handleException3({
        writeln("asd");
    });
}

-- 
/Jacob Carlborg
May 22, 2018
On 05/22/2018 11:20 AM, Robert M. Münch wrote:
> I see that I'm writing
> 
> try {
> ... different code ...
> } catch (myException e) {
>      ... same handling code ...
> }
> 
> over and over again.
> 
> Of course I can put the exception handling code into a function to not duplicate it. However, I still need to write this construct over and over again. Is there a way to handle it more generic? Like:
> 
> ??? (... code ...);
> 
> or
> 
> ??? { ... code ...};
> 
> Where ??? would do the try and re-use the exception handling code everytime? I hope this is understandable.
> 

An idiom known in C++ circles is a Lippincott function:

  https://cppsecrets.blogspot.ca/2013/12/using-lippincott-function-for.html

Just wanted to mention that it can be a part of a clean solution.

Ali
May 23, 2018
On Tuesday, 22 May 2018 at 18:20:43 UTC, Robert M. Münch wrote:
> I see that I'm writing
>
> try {
> ... different code ...
> } catch (myException e) {
> 	... same handling code ...
> }
>
> over and over again.
>
> Of course I can put the exception handling code into a function to not duplicate it. However, I still need to write this construct over and over again. Is there a way to handle it more generic? Like:
>
> ??? (... code ...);
>
> or
>
> ??? { ... code ...};
>
> Where ??? would do the try and re-use the exception handling code everytime? I hope this is understandable.

Have you looked at std.exception.ifThrown? You could define a handler function and pass it to ifThrown for each expression that may throw. If it's a statement, I believe you could wrap it in a lambda which is immediately called, then append .ifThrown(handler).
May 23, 2018
On 2018-05-22 18:34:34 +0000, Ali ‡ehreli said:

> An idiom known in C++ circles is a Lippincott function:
> 
>    https://cppsecrets.blogspot.ca/2013/12/using-lippincott-function-for.html
> 
> Just wanted to mention that it can be a part of a clean solution.

Thanks, and I assume that D has the same property WRT exception re-throwing as C++, right?

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster

May 23, 2018
On 05/23/2018 12:47 AM, Robert M. Münch wrote:
> On 2018-05-22 18:34:34 +0000, Ali ‡ehreli said:
> 
>> An idiom known in C++ circles is a Lippincott function:
>>
>>    https://cppsecrets.blogspot.ca/2013/12/using-lippincott-function-for.html
>>
>> Just wanted to mention that it can be a part of a clean solution.
> 
> Thanks, and I assume that D has the same property WRT exception re-throwing as C++, right?
> 

I think you have to catch and rethrow explicitly:

import std.stdio;

void main() {
    try {
        try {
            throw new Exception("Yo");
        } catch (Exception e) {
            writeln("Rethrowing");
            throw e;
        }
    } catch (Exception e) {
        writeln(e.msg);
    }
}

Rethrowing
Yo

Keeping in mind that it's possible to catch Throwable as well but it's considered less sanitary because it would catch Errors as well, which is supposed to mean "unrecoverable error". There are long discussions about whether one should do that or not...

Ali
May 23, 2018
On Wednesday, May 23, 2018 04:07:25 Ali Çehreli via Digitalmars-d-learn wrote:
> On 05/23/2018 12:47 AM, Robert M. Münch wrote:
> > On 2018-05-22 18:34:34 +0000, Ali ‡ehreli said:
> >> An idiom known in C++ circles is a Lippincott function:
> >>
> >>
> >> https://cppsecrets.blogspot.ca/2013/12/using-lippincott-function-for.ht ml
> >>
> >> Just wanted to mention that it can be a part of a clean solution.
> >
> > Thanks, and I assume that D has the same property WRT exception re-throwing as C++, right?
>
> I think you have to catch and rethrow explicitly:
>
> import std.stdio;
>
> void main() {
>      try {
>          try {
>              throw new Exception("Yo");
>          } catch (Exception e) {
>              writeln("Rethrowing");
>              throw e;
>          }
>      } catch (Exception e) {
>          writeln(e.msg);
>      }
> }
>
> Rethrowing
> Yo
>
> Keeping in mind that it's possible to catch Throwable as well but it's considered less sanitary because it would catch Errors as well, which is supposed to mean "unrecoverable error". There are long discussions about whether one should do that or not...

The short answer to that would be that you should never do it. The long answer gets considerably more complicated, and while it _can_ make sense under certain circumstances when you're very careful, it's a minefield of potential problems such that no one who who isn't a very advanced D user who really knows what they're doing should even consider it. Increasingly, I tend to think that D should not have had Errors or any Throwables other than exceptions and should have just printed something useful and exited in a way that created a core dump in any case that's supposed to be non-recoverable. :|

Either way, I think that we should be clear that doing anything involving catching anything that isn't an Exception or derived from Exception is fraught with peril and only for advanced users.

- Jonathan M Davis


May 24, 2018
On 2018-05-22 18:33:06 +0000, Jacob Carlborg said:

> You can always create a function that takes a delegate or lambda and handles the exception in the function. Here are three versions of the same thing, depending on how you want the call site to look like.

Hi, great! Thanks for the examples... BTW: Is there a place where such generic and fundamental examples are collected?

> void handleException1(alias dg)()
> {
>      try dg();
>      catch (Exception e) { /* handle exception */ }
> }
> 
> void handleException2(lazy void dg)
> {
>      try dg();
>      catch (Exception e) { /* handle exception */ }
> }
> 
> void handleException3(scope void delegate () dg)
> {
>      try dg();
>      catch (Exception e) { /* handle exception */ }
> }
> 
> void main()
> {
>      handleException1!({
>          writeln("asd");
>      });
> 
>      handleException1!(() => writeln("asd"));
> 
>      handleException2(writeln("asd"));
> 
>      handleException3({
>          writeln("asd");
>      });
> }

What is exactly the difference between handleException1 and 3?

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster

May 24, 2018
On 2018-05-24 08:05, Robert M. Münch wrote:

> Hi, great! Thanks for the examples... BTW: Is there a place where such generic and fundamental examples are collected?

Not as far as I know.

>> void handleException1(alias dg)()
>> {
>>      try dg();
>>      catch (Exception e) { /* handle exception */ }
>> }
>>
>> void handleException2(lazy void dg)
>> {
>>      try dg();
>>      catch (Exception e) { /* handle exception */ }
>> }
>>
>> void handleException3(scope void delegate () dg)
>> {
>>      try dg();
>>      catch (Exception e) { /* handle exception */ }
>> }
>>
>> void main()
>> {
>>      handleException1!({
>>          writeln("asd");
>>      });
>>
>>      handleException1!(() => writeln("asd"));
>>
>>      handleException2(writeln("asd"));
>>
>>      handleException3({
>>          writeln("asd");
>>      });
>> }
> 
> What is exactly the difference between handleException1 and 3?

With handleException1 the delegate needs to be passed as a template argument, in the other case as a regular argument. I thought that the lambda syntax, () => writeln("asd"), did not work as a regular argument, but I checked now and it does.

Passing it as a template argument might allow the compiler to inline it. All range functions in Phobos are using template argument approach.

-- 
/Jacob Carlborg
May 24, 2018
On Thursday, May 24, 2018 19:39:07 Jacob Carlborg via Digitalmars-d-learn wrote:
> On 2018-05-24 08:05, Robert M. Münch wrote:
> > Hi, great! Thanks for the examples... BTW: Is there a place where such generic and fundamental examples are collected?
>
> Not as far as I know.
>
> >> void handleException1(alias dg)()
> >> {
> >>      try dg();
> >>      catch (Exception e) { /* handle exception */ }
> >> }
> >>
> >> void handleException2(lazy void dg)
> >> {
> >>      try dg();
> >>      catch (Exception e) { /* handle exception */ }
> >> }
> >>
> >> void handleException3(scope void delegate () dg)
> >> {
> >>      try dg();
> >>      catch (Exception e) { /* handle exception */ }
> >> }
> >>
> >> void main()
> >> {
> >>      handleException1!({
> >>          writeln("asd");
> >>      });
> >>
> >>      handleException1!(() => writeln("asd"));
> >>
> >>      handleException2(writeln("asd"));
> >>
> >>      handleException3({
> >>          writeln("asd");
> >>      });
> >> }
> >
> > What is exactly the difference between handleException1 and 3?
>
> With handleException1 the delegate needs to be passed as a template argument, in the other case as a regular argument. I thought that the lambda syntax, () => writeln("asd"), did not work as a regular argument, but I checked now and it does.
>
> Passing it as a template argument might allow the compiler to inline it. All range functions in Phobos are using template argument approach.

With a template alias, it will accept pretty much any symbol (which would then normally be restricted by a template constraint so that it's a symbol which is usable in the target context), whereas an explicit delegate will only accept anything that implicitly converts to a delegate with that signature. What matches, I don't know, since I pretty much enver declare explicit delegates, though I don't find it surprising that a lambda works, since that's basically what a lambda is. But I expect that if you did something like pass a functor, it would work with the alias but wouldn't work with the delegate parameter.

- Jonathan M Davis