Difference Between Try-Catch Syntax for Function

Difference between try-catch syntax for function

The First Syntax:

The scope of the try block starts after the Member Initialization list has been completed, So any exception thrown during Member Initialization will not be caught by this try-catch block.

The second syntax:

It ensures that if an exception gets thrown during Member Initialization list then you are able to catch the exception.

The Third Syntax:

It ensures that any exception thrown from betwen the starting brace of the try block inside the function body gets caught appropriately, It would mean any exception caused during the argument passing(if any can occur) will not be caught in this try-catch block.

So yes they are disinctly different in what functionality they provide.


EDIT:

Some guidelines to be considered while using the second syntax(function-try-block) in constructors & destructors:

As per the C++ Standard,

If the catch block does not throw (either rethrow the original exception, or throw something new), and control reaches the end of the catch block of a constructor or destructor, then the original exception is automatically rethrown.

In Simple words:

A constructor or destructor function-try-block's handler code MUST finish by emitting some exception.

Guideline 1:

Constructor function-try-block handlers have only one purpose -- to translate an exception. (And maybe to do logging or some other side effects.) They are not useful for any other purpose.

Throwing a exception from destructors is an bad idea, Take a look here to know why.

Guideline 2:

Destructor function-try-blocks have no practical use at all. There should never be anything for them to detect, and even if there were something to detect because of evil code, the handler is not very useful for doing anything about it because it can not suppress the exception.

Guideline 3:

Always clean up unmanaged resource acquisition in local try-block handlers within the constructor or destructor body, never in constructor or destructor function-try-block handlers.


For Standardese Fans:

C++ standard, clause 15.3, paragraph 15:

If a return statement appears in a handler of the function-try-block of a constructor, the program is ill-formed.

C++ standard, clause 15.3, paragraph 16:

The exception being handled is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. Otherwise, a function returns when control reaches the end of a handler for the function-try-block (6.6.3). Flowing off the end of a function-try-block is equivalent to a return with no value; this results in undefined behavior in a value-returning function (6.6.3).


References:

Have a look at this must read resource here for more details & explanation.

function try catch syntax and main

The standard does not forbid its usage within [basic.start.main], and, while forcing all implementations to support at least int main() {/*...*/ } and int main(int argc, char* argv[]) {/*...*/}, does not limit implementations to those two declarations (3.6.1, para. 2).

From that in isolation, it would appear at the least that it is legal, though of course that relates only to function-declarations, not function-definitions.

Reading on, [except.handle], paragraph 13 states the following:

Exceptions thrown in destructors of objects with static storage
duration or in constructors of namespace-scope objects are not caught
by a function-try-block on main(). (15.3 para. 13)

It makes specific mention of a function-try-block placed on main(), which strongly implies that such a structure is legal and has defined behavior. Adding in the information that main() is only special in its name and return type, and that implementations may not overload it to alter any behavior, makes a pretty strong case that it acts in a normal fashion except when specially noted such as in the above quote. In other words, yes, it is legal and well-defined.

The blog post I supplied in the first version of this answer actually does a good job of illustrating the rules given by the above blockquote, so I'll retain the link to it, even though it does not directly discuss the issue in the OP's question.

Regarding a comment on the OP, you can issue return statements within a function-try-block, and [except.handle] has this to say:

Flowing off the end of a function-try-block is equivalent to a return
with no value; this results in undefined behavior in a value-returning
function (6.6.3). (15.3 para. 15)

If you're in a catch-block at the end of main, you're not going to flow over the function's body (which would be the try-block in this case), so the rule that main automatically calls return 0; on flowover doesn't apply. You need to return some int (quite possibly an error code) to keep from becoming undefined.

C++ - Do not understand the syntax of the catch function in try-catch


catch (const std::exception& e) { /* */ }

You need to use a named exception if you want to be able to access the exception object in the catch block. For example if you wanted to print e.what().

catch (const std::exception&) { /* */ }

You can use an un-named exception if you don't need to access the exception object. Maybe you don't care about the contents of the exception, and you just want to handle all exceptions (of this type) in the same way.

catch (...) { /* */ }

C++ allows you to throw an object of any type, whether or not it is of a type derived from std::exception. This type of catch block will catch anything thrown. Just like the un-named exception, you will not have access to the thrown object. But also, you won't even have any way to know what type it is.

For your final example, I don't think it is your catch block that is printing the message. After all, you're catching the wrong type. Some compilers will automatically insert exception catching code that will catch anything that escapes main, and if it is a type derived from std::exception, it will print the result of what(), before terminating the program. I believe GCC does this, but Visual Studio does not. Not sure about Clang or any other compilers.

What is the purpose of a function try block?

Imagine if UseResources was defined like this:

class UseResources
{
class Cat *cat;
class Dog dog;

public:
UseResources() : cat(new Cat), dog() { cout << "UseResources()" << endl; }
~UseResources() { delete cat; cat = NULL; cout << "~UseResources()" << endl; }
};

If Dog::Dog() throws, then cat will leak memory. Becase UseResources's constructor never completed, the object was never fully constructed. And therefore it does not have its destructor called.

To prevent this leak, you must use a function-level try/catch block:

UseResources() try : cat(new Cat), dog() { cout << "UseResources()" << endl; } catch(...)
{
delete cat;
throw;
}

To answer your question more fully, the purpose of a function-level try/catch block in constructors is specifically to do this kind of cleanup. Function-level try/catch blocks cannot swallow exceptions (regular ones can). If they catch something, they will throw it again when they reach the end of the catch block, unless you rethrow it explicitly with throw. You can transform one type of exception into another, but you can't just swallow it and keep going like it didn't happen.

This is another reason why values and smart pointers should be used instead of naked pointers, even as class members. Because, as in your case, if you just have member values instead of pointers, you don't have to do this. It's the use of a naked pointer (or other form of resource not managed in a RAII object) that forces this kind of thing.

Note that this is pretty much the only legitimate use of function try/catch blocks.


More reasons not to use function try blocks. The above code is subtly broken. Consider this:

class Cat
{
public:
Cat() {throw "oops";}
};

So, what happens in UseResources's constructor? Well, the expression new Cat will throw, obviously. But that means that cat never got initialized. Which means that delete cat will yield undefined behavior.

You might try to correct this by using a complex lambda instead of just new Cat:

UseResources() try
: cat([]() -> Cat* try{ return new Cat;}catch(...) {return nullptr;} }())
, dog()
{ cout << "UseResources()" << endl; }
catch(...)
{
delete cat;
throw;
}

That theoretically fixes the problem, but it breaks an assumed invariant of UseResources. Namely, that UseResources::cat will at all times be a valid pointer. If that is indeed an invariant of UseResources, then this code will fail because it permits the construction of UseResources in spite of the exception.

Basically, there is no way to make this code safe unless new Cat is noexcept (either explicitly or implicitly).

By contrast, this always works:

class UseResources
{
unique_ptr<Cat> cat;
Dog dog;

public:
UseResources() : cat(new Cat), dog() { cout << "UseResources()" << endl; }
~UseResources() { cout << "~UseResources()" << endl; }
};

In short, look on a function-level try-block as a serious code smell.

How to decide between using if/else vs try/catch?

You should never use try/catch for flow control.

Generating an exception is an extremely expensive action. If/else is much faster and cleaner.

When should I use try catch instead of then catch?

The difference is in how you're handing Promises.

If you're using await to handle the Promise then you wrap it in a try/catch. Think of await as a way to make asynchronous operations semantically similar to synchronous operations.

But if you're not using await and are instead handling the Promise by appending .then() to it then you'd append a .catch() to that chain to catch failures from within the asynchronous operation.

Because a try/catch isn't going to catch an exception that happens from within the asynchronous operation if that operation isn't awaited.

Why wouldn't you declare main() using a function-try-block?

You can easily convert

int main() try {
// The real code of main
}
catch (...)
{
}

to

int realMain()
{
// The real code of main
}

int main()
{
try
{
return realMain();
}
catch ( ... )
{
}
}

without losing functionality/behavior.

I am going to guess that whether you use the first version or the second version is a matter of coding practices of a team. From a compiler and run time standpoint, I don't see any semantic difference.

Thorough use of 'if' statements or 'try/catch' blocks?

this debate (2003) was good:

http://www.joelonsoftware.com/items/2003/10/13.html

http://nedbatchelder.com/blog/200310/joel_on_exceptions.html

Correct Try...Catch Syntax Using Async/Await


It seems to be best practice not to place multiple lines of business logic in the try body

Actually I'd say it is. You usually want to catch all exceptions from working with the value:

try {
const createdUser = await this.User.create(userInfo);

console.log(createdUser)
// business logic goes here
} catch (error) {
console.error(error) // from creation or business logic
}

If you want to catch and handle errors only from the promise, you have three choices:

  • Declare the variable outside, and branch depending on whether there was an exception or not. That can take various forms, like

    • assign a default value to the variable in the catch block
    • return early or re-throw an exception from the catch block
    • set a flag whether the catch block caught an exception, and test for it in an if condition
    • test for the value of the variable to have been assigned
      let createdUser; // or use `var` inside the block
    try {
    createdUser = await this.User.create(userInfo);
    } catch (error) {
    console.error(error) // from creation
    }
    if (createdUser) { // user was successfully created
    console.log(createdUser)
    // business logic goes here
    }
  • Test the caught exception for its type, and handle or rethrow it based on that.

      try {
    const createdUser = await this.User.create(userInfo);
    // user was successfully created
    console.log(createdUser)
    // business logic goes here
    } catch (error) {
    if (error instanceof CreationError) {
    console.error(error) // from creation
    } else {
    throw error;
    }
    }

    Unfortunately, standard JavaScript (still) doesn't have syntax support for conditional exceptions.

    If your method doesn't return promises that are rejected with specific enough errors, you can do that yourself by re-throwing something more appropriate in a .catch() handler:

      try {
    const createdUser = await this.User.create(userInfo).catch(err => {
    throw new CreationError(err.message, {code: "USER_CREATE"});
    });

    } …

    See also Handling multiple catches in promise chain for the pre-async/await version of this.

  • Use then with two callbacks instead of try/catch. This really is the least ugly way and my personal recommendation also for its simplicity and correctness, not relying on tagged errors or looks of the result value to distinguish between fulfillment and rejection of the promise:

      await this.User.create(userInfo).then(createdUser => {
    // user was successfully created
    console.log(createdUser)
    // business logic goes here
    }, error => {
    console.error(error) // from creation
    });

    Of course it comes with the drawback of introducing callback functions, meaning you cannot as easily break/continue loops or do early returns from the outer function.



Related Topics



Leave a reply



Submit