Jump to content

  • Log In with Google      Sign In   
  • Create Account


Inline try/catch


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
21 replies to this topic

#1 HappyCoder   Members   -  Reputation: 2561

Like
0Likes
Like

Posted 01 August 2013 - 10:57 AM

So I just wanted to share an idea I have been juggling around in my head and wondered what you thought of it, if there is a language that implements this, and if you would use it.

I call it inline try catch. It is like the blend between try/catch and the ternary ? operator. An example of how to use it.

//block try catch
string result = null; 
try
{
    result = bar.getStringValue();
}
catch (BarException)
{
    result = "Default string";
}

// inline try catch
string result = try bar.getStringValue() catch(BarExeption) "Default string";
What I like about this is the try catch block returns a value, this would reduce branch complexity but at the cost of having longer line length. I could see the inline try being quite ugly if care isn't taken to keep it simple.

Any thoughts?

Sponsor:

#2 ultramailman   Prime Members   -  Reputation: 1563

Like
0Likes
Like

Posted 01 August 2013 - 11:58 AM

This looks like a convenient syntax to have, but what do you mean it would reduce branch complexity? I imagine a compiler would translate it into the same try catch block it represents.



#3 samoth   Crossbones+   -  Reputation: 4684

Like
1Likes
Like

Posted 01 August 2013 - 12:29 PM

That's an example of expection handling (no, that's not a typo).

 

While this certainly "works", most people will consider it an abuse of the language. Also, it will certainly not reduce branch complexity, on the contrary. It will have the same branch complexity (only you don't see it!) plus RTTI overhead plus stack unwinding overhead.

 

Personally, I would prefer bar.getStringValue() to return a default string if it can't provide anything better. This also has no branches (only hidden inside the class, but not visible to the user), and it feels "cleaner". But of course that's a bit of a matter of taste, too.


Edited by samoth, 01 August 2013 - 12:35 PM.


#4 HappyCoder   Members   -  Reputation: 2561

Like
0Likes
Like

Posted 01 August 2013 - 01:06 PM

That's an example of expection handling (no, that's not a typo).
 
While this certainly "works", most people will consider it an abuse of the language. Also, it will certainly not reduce branch complexity, on the contrary. It will have the same branch complexity (only you don't see it!) plus RTTI overhead plus stack unwinding overhead.
 
Personally, I would prefer bar.getStringValue() to return a default string if it can't provide anything better. This also has no branches (only hidden inside the class, but not visible to the user), and it feels "cleaner". But of course that's a bit of a matter of taste, too.


I realize that the generated code would be similar if not identical. It would reduce branch complexity of the source code for somebody who has to read and maintain it. And yes, in this particular case try catch is probably not the best way to handle having a default string. There is probably a better example to be used here.

Edited by HappyCoder, 01 August 2013 - 01:06 PM.


#5 smr   Members   -  Reputation: 1623

Like
0Likes
Like

Posted 01 August 2013 - 01:14 PM

I agree that it could be convenient in some situations, but it can be implemented without changes to some languages. Here's a quick example in C#:

 

(keep in mind that these examples are contrived, and in this situation it'd be much cleaner to do Console.WriteLine(t == null ? "" : (t.ToString() ?? ""))

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Thing t = null;

            // t is null so the fail delegate is called
            Console.WriteLine(InlineTryCatch(()=> t.ToString(), ()=> "Fail 1 (OK)"));

            // t is null, but there's no fail so default(T) is returned
            Console.WriteLine(InlineTryCatch(() => t.ToString(), null));

            // t is null and a NullReferenceException is thrown. That's the one we're looking for, so fail delegate is called
            Console.WriteLine(InlineTryCatch<string, NullReferenceException>(() => t.ToString(), (ex) => "Fail 2 (OK)"));

            try
            {
                // t is null, but the exception thrown doesn't match. because it's not a matching exception
                // the exception is rethrown
                Console.WriteLine(InlineTryCatch<string, IndexOutOfRangeException>(() => t.ToString(), (ex) => "Fail 3 (OK)"));
            }
            catch
            {
                Console.WriteLine("Exception 1");
            }

            // no exception so the success delegate is called
            t = new Thing();
            Console.WriteLine(InlineTryCatch(() => t.ToString(), () => "fail 4"));
        }

        class Thing
        {
            public override string ToString()
            {
                return "Win!";
            }
        }

        static T InlineTryCatch<T, U>(Func<T> func, Func<U, T> fail) where U : Exception
        {
            try
            {
                return func();
            }
            catch (U ex)
            {
                if (fail != null)
                {
                    return fail(ex);
                }
                else
                {
                    throw ex;
                }
            }
        }

        static T InlineTryCatch<T>(Func<T> func, Func<T> fail)
        {
            return InlineTryCatch<T, Exception>(func, (ex) => fail == null ? default(T) : fail());
        }
    }
}
 

Edited by smr, 01 August 2013 - 01:18 PM.


#6 frob   Moderators   -  Reputation: 20238

Like
0Likes
Like

Posted 01 August 2013 - 01:22 PM

Many languages have a null coalescing operator.

 

Basically it says "If this value is non-null, use it.  If it is null, use this instead".


Check out my personal indie blog at bryanwagstaff.com.

#7 smr   Members   -  Reputation: 1623

Like
0Likes
Like

Posted 01 August 2013 - 01:26 PM

Many languages have a null coalescing operator.

 

Basically it says "If this value is non-null, use it.  If it is null, use this instead".

 

And used along with the ternery if operator it pretty much covers all the cases when you'd want to do the inline try/catch.

 

t == null ? "" : (t.ToString() ?? "")



#8 rip-off   Moderators   -  Reputation: 8113

Like
0Likes
Like

Posted 01 August 2013 - 01:48 PM

Many dynamic languages allow you to write code like the following:

result = (bar.getStringValue() or "Default string")
result = bar.getStringValue() || "Default string"

In some, statement are also expression, and the last evaluated value is the overall result: 

def foo()
    if rand() < 0.5
        return "Yay!"
    end
    raise "Oh no"
end
 
x = begin foo() rescue "Default string" end

Edited by rip-off, 01 August 2013 - 01:50 PM.


#9 dmatter   Crossbones+   -  Reputation: 3030

Like
1Likes
Like

Posted 02 August 2013 - 11:32 AM

In many languages (Java in particular - I hate checked exceptions) I feel exception handling is somewhat lackluster.
 
One thing I dislike is having to push a variable declaration up, outside of a try-catch block and assigning null to it, like in HappyCoder's original example (and for value types there might not be a sensible initial value to give it).
 
Perhaps try-catch shouldn't define a scope at all? Initially that seems unworkable if you consider that an exception might cause some vars to not get declared at all. However most catch-blocks don't really handle the error, they just wrap more detail around an exception and re-throw, perhaps with some logging thrown in and occasionally they might also do some cleanup, but either way the stack is about to unwind.

 

Given this, maybe there should be a separate syntax for that use-case: An all-or-nothing try-catch. If it fails you are given a chance to intercept it but ultimately the containing function will exit after that. If it succeeds then any vars declared in the try-block are still kept in-scope for subsequent code to use.

 

I'd be more interested in seeing something like that than an inline try-catch as proposed here.

 

Lacking that you end up either lumping code that didn't really need to be in the try-catch into the try-block. Or, if you only need one variable out, you end up extracting the try-catch into it's own function.


Edited by dmatter, 02 August 2013 - 11:35 AM.


#10 DemonRad   Members   -  Reputation: 290

Like
-1Likes
Like

Posted 02 August 2013 - 12:32 PM

It sounds like a automatic-rethrow exception.

try{


}
catch(AllOrNothing by_value){
 // your interception code
 } 

// the magic class (this is just a draft

class AllOrNothing: public virtual std::exception{

public: 

    virtual ~AllOrNothing(){
        if(!flag)
            throw this->clone(~flag);
    }

};


when the catch block goes out of scope the argument is destroyed (if passed by value) and it throws. Of course it sets a flag when cloning so that it rethrows only once.


Edited by DemonRad, 02 August 2013 - 12:46 PM.

Peace and love, now I understand really what it means! Guardian Angels exist! Thanks!


#11 Paradigm Shifter   Crossbones+   -  Reputation: 5253

Like
0Likes
Like

Posted 02 August 2013 - 12:53 PM

But you never ever want to catch anything by value (slicing) - assuming C++ of course. You always want to throw by value and catch by reference, or else bad things may happen.


"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

#12 DemonRad   Members   -  Reputation: 290

Like
0Likes
Like

Posted 19 August 2013 - 11:30 AM

You already have stack space for that. of course assuming the exception do not allocate extra memory in copy ctor...


Edited by DemonRad, 19 August 2013 - 11:34 AM.

Peace and love, now I understand really what it means! Guardian Angels exist! Thanks!


#13 Cornstalks   Crossbones+   -  Reputation: 6974

Like
0Likes
Like

Posted 19 August 2013 - 11:32 AM

You already have stack space for that

Stack space for what?


[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#14 SiCrane   Moderators   -  Reputation: 9559

Like
1Likes
Like

Posted 19 August 2013 - 11:51 AM

Note that throwing an exception from a destructor is also super bad mojo in C++. Basically any exception guarantees go out the window if you have destructors that throw. It's sufficiently bad that C++11 gave all destructors an implicit noexcept exception specification, meaning that an exception from a destructor will immediately call terminate() unless you specifically enable exceptions for the destructor for the class or one of the subobjects of that class contains a destructor which has exception enabled destructors.



#15 DemonRad   Members   -  Reputation: 290

Like
0Likes
Like

Posted 20 August 2013 - 06:42 AM

 

You already have stack space for that

Stack space for what?

 

For passing by value.


Peace and love, now I understand really what it means! Guardian Angels exist! Thanks!


#16 Paradigm Shifter   Crossbones+   -  Reputation: 5253

Like
1Likes
Like

Posted 20 August 2013 - 06:50 AM

You still don't want to catch by value though, since the object will be sliced if it is a derived class and you catch the base class.

 

See: http://stackoverflow.com/questions/2522299/c-catch-blocks-catch-exception-by-value-or-reference


"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

#17 samoth   Crossbones+   -  Reputation: 4684

Like
1Likes
Like

Posted 20 August 2013 - 07:12 AM

Note that throwing an exception from a destructor is also super bad mojo in C++. Basically any exception guarantees go out the window if you have destructors that throw. It's sufficiently bad that C++11 gave all destructors an implicit noexcept exception specification, meaning that an exception from a destructor will immediately call terminate() unless you specifically enable exceptions for the destructor for the class or one of the subobjects of that class contains a destructor which has exception enabled destructors.

 

Actually I find this one of the more valuable additions to C++11, because if your destructor throws, something is seriously fucked up. At least, usually... there may be singular cases where it may be allowable and desirable, but I must admit I don't see them. I do see the problems, though.

 

Either you have a "conditional destructor" with some (hopefully bulletproof) logic that prevents it from throwing while being unwound, then your class behaves differently although the expectation would be that it works the same. You now need a separate way of detecting the error condition, or you must simply ignore the error. Either way, this stinks.

 

Or, you are unaware of what happens when you throw from a destructor during unwind, and do it anyway. This is even worse, in two respects (first, because you don't know the implications, and second because it actually happens, and the program is now in an undefined, unrecoverable state). And, what's worst, now you don't even know what happened, or where.

 

So, for that this case, crashing hard by terminating the program is a good thing. It forces the programmer to address the issue. Actually, the mere presence of the throw keyword inside a destructor should preferrably generate a build error, if you ask me.


Edited by samoth, 20 August 2013 - 07:17 AM.


#18 King Mir   Members   -  Reputation: 1951

Like
2Likes
Like

Posted 20 August 2013 - 08:47 AM

One problem I see with inlineing try catch like that is that it makes it too easy to use. Throwing and catching an exception is an expensive operation, and it's good when the code reflects that complexity. You don't want to encourage non exceptional cases to throw exceptions for the sake of convenient syntax.

#19 Khatharr   Crossbones+   -  Reputation: 2960

Like
0Likes
Like

Posted 20 August 2013 - 05:07 PM

Ruby allows this with a simpler syntax.

 

'Normal' exception handling:

begin
  10 / 0
rescue
  puts "Please do not divide by zero."
ensure #the 'ensure' section will always execute when the block exits, regardless of exceptions
  puts "Moving right along..."
end

'Inline' exception handling:

10 / 0 rescue puts "Please do not divide by zero."
puts "Moving right along..."

In the 'inline' case, the handler extends to the end of the line. You can have multiple statements if you use semicolons to separate them.


Edited by Khatharr, 20 August 2013 - 05:11 PM.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

#20 l0calh05t   Members   -  Reputation: 689

Like
0Likes
Like

Posted 21 August 2013 - 02:08 AM

Note that throwing an exception from a destructor is also super bad mojo in C++. Basically any exception guarantees go out the window if you have destructors that throw. It's sufficiently bad that C++11 gave all destructors an implicit noexcept exception specification, meaning that an exception from a destructor will immediately call terminate() unless you specifically enable exceptions for the destructor for the class or one of the subobjects of that class contains a destructor which has exception enabled destructors.


Since all guarantees go out of the window if you throw in the destructor it is a *good thing* that destructors are marked noexcept, that way you have a guarantee that terminate is called, which I'll take over all kinds of undefined behavior (which throwing in the destructor will cause when used in conjunction with pretty much any library class or template which doesn't expect that... which includes all standard containers) any day.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS