'Why is there std::logic_error?

I have been told that you will only throw exceptions to handle "external problems", like problems that are not internal, i.e., only with run-time problems. But I just discovered the std::logic_error exception, and I started asking myself: Why has it been created when you most likely not would throw an exception rather than using assert or just internal logic, like if/else statement?

c++


Solution 1:[1]

Consider this code snippet, part of a million of lines of code product:

enum class Source_type
{
   type_a, type_b, type_c
};

class Resource
{
    Resource(const std::string& param1, int val1, int val2);
    Resource(Handle h1, int val1, int val2);
    Resource(Resource_builder rb, int v1, int v2);
};

Resouce create_resource_from_source(Source_type type, int v1, int v2)
{
   switch (type)
   {
       case Source_type::type_a:
       {
           std::string s = ...;
           return Resource{s, v1, v2};
       }
       case Source_type::type_b:
       {
           Handle h = ...;
           return Resource{h, v1, v2};
       }
       case Source_type::type_c;
       {
           Resource_builder b = ...;
           return Resouce{b, v1, v2};
       }
       default:
       {
           // should never reach here

           // however experience has painfully tough us that
           // leaving things that should never happen unchecked
           // will come back and bite us later

           // so what do we put here? <-----
           break;
        }
   }
};

One solution would be to use an assert as you have suggested:

       default:
           // should never reach here

           assert(false);
           break;

However this will hard crash your application. Or worse, as in release build this would lead to Undefined Behavior because the assert is never checked and you reached a branch from which you don't return. Depending on your scenario this could be unacceptable. For instance if there is very important that your application does not crash, even in the event of such a bug. Here come exceptions into play:

  default:
           // should never reach here

           throw std::logic_exception{...}; // or custom exception
           break;

By throwing an exception you now have options to gracefully recover:

void some_ui_callback()
{
   Source_type type = ...;

   try
   {
       Resource r= create_resource_from_source(type, a, b);
   }
   catch(std::logic_exception& e)
   {
       showUserDialog("internal error. Please try again by selecting another option."
                      "Please submit a bug report...blah.. blah");
   }

   // or

   catch(std::logic_exception& e)
   {
      Resource r = create_fallback_resource(a, b);
   }
}

Solution 2:[2]

In languages supporting assertions, you are true that the assertion mechanism would be used instead of logic_error exceptions.

In c++ < c++20, there is no such a true assert feature, that's why the logic_error has been created. Keep in mind that assert() is removed in release for instance.

In c++ 20, with the addition of 'contracts', the [[assert : expr]] could be used instead of the logic_error exception.

You can refer to Herb Sutter's article https://herbsutter.com/2018/07/02/trip-report-summer-iso-c-standards-meeting-rapperswil/ for more explanation.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 bolov