Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why throw a class over an enum?

Tags:

c++

exception

Just wondering, why is it better to throw a class over an enum

Surely throwing classes is more overhead?

e.g.

enum MyException
{
   except_a,
   except_b,
   except_c
}


void function f(){
  throw except_a;
}


int main(int arc, char* argv[]){
  
  try{

  } catch (MyException e){
    switch(e){
      except_a: break;
      except_b: break;
      except_c: break;
    }
  }

  return 0;
}

Apart from the overhead. I also need to declare a class for each one which might override std::exception or something. More code, larger binary... what's the benefit?

like image 709
hookenz Avatar asked Feb 19 '11 03:02

hookenz


People also ask

What are the benefits of using a sealed class over enum?

In a sealed class, we can simply add multiple custom constructors depending on what we need. Furthermore, we can define multiple functions with different names, parameters, and return types. In an enum class, however, we can't define different functions in each enum constant.

Should enums have their own class?

If the enum is only used within one class, it should be placed within that class, but if the enum is used between two or more classes, it ought to either be in it's own code file, or in a conglomerated code file that contains all the enum for a particular assembly.

Why do we use enum classes?

Use enums when you have values that you know aren't going to change, like month days, days, colors, deck of cards, etc.

What is the difference between enum and enum class in C++?

C++11 has introduced enum classes (also called scoped enumerations), that makes enumerations both strongly typed and strongly scoped. Class enum doesn't allow implicit conversion to int, and also doesn't compare enumerators from different enumerations. To define enum class we use class keyword after enum keyword.


2 Answers

Which of the following two catch blocks is easier to understand:

try {
    do_something();
}
catch (const int&) {
    // WTF did I catch?
}
catch (const std::out_of_range&) {
    // Oh, something was out of range!
}

The name of an exception class should tell you something about why the exception was thrown; int doesn't tell you anything, you just know that you caught an int, whatever that means.


To consider your updated example of using an enumeration instead of an integer, which of the following is clearer:

try{
    do_something();
} 
// (1) Catch an enum:
catch (MyException e) {
    switch(e) {
    except_a: break;
    except_b: break;
    except_c: break;
    default:  throw; // Don't forget, you have to throw any exceptions 
                     // that you caught but don't actually want to catch!
    }
}
// (2) Catch specific exceptions
catch (const ExceptionA&) { }
catch (const ExceptionB&) { }
catch (const ExceptionC&) { }

There is no reason at all to prefer the first form: there's no performance benefit and the code is less clear and more error-prone. In your example, you forgot to rethrow the exception if you weren't handling it, so if someone later added an except_d exception to the MyException enumeration, you'd have unknowingly caught it.


As for your "overhead" question, it depends, but probably not. It should be (relatively) rare that an exception is thrown, and if you have a tight loop where performance really matters, you aren't going to be using exceptions anyway.

The benefit of using class hierarchies for exceptions is that they allow you to write clearer code, just like the benefit of using non-local control flow (exceptions) instead of other approaches like error code return values allows you to write clearer code (at least when you use exceptions correctly).

like image 88
James McNellis Avatar answered Sep 21 '22 05:09

James McNellis


Given

enum MyException
{
   except_a,
   except_b,
   except_c
}

write a catch clause that only catches except_c exceptions.

With

struct my_except {};
struct my_except_a : my_except {};
struct my_except_b : my_except {};
struct my_except_c : my_except {};

that's easy, since you can catch the base class or derived classes.

Many of the common advantages of derivation apply to exceptions. For example, base classed can stand in for derived classes and code needs to know only base classes to deal with derived exceptions. That's a form of polymorphism, while the enum is a switch over a type.

The general rule about polymorphism applies here, too: Whenever you are tempted to use a switch over a type, you are dismissing the advantages of polymorphism. The problems you are getting yourself into will be seen once the code has expanded to hundreds of kLoC, and you need to add a new type. With polymorphism, that's easy, because most code will be fine dealing with base class references only.
With the type enum, you have to hunt down every single switch statement over that enum and check whether you need to adapt it.

Things like these have killed more than one company.


Here's an afterthought: When they've done this for a while, users usually start to add all kinds of data to their exception types. A classic is to take __FILE__ and __LINE__ in an exception's constructor to be able to see where an exception came from. But this, too, needs exceptions to be class types.

like image 39
sbi Avatar answered Sep 21 '22 05:09

sbi