Jump to content
  • Advertisement
Sign in to follow this  
HappyCoder

Inline try/catch

This topic is 1739 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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?

Share this post


Link to post
Share on other sites
Advertisement

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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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() ?? "")

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!