Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

deriving from std::exception

i want to derive from std::exception to add specific information to my log files, but i cant figure how to access the .what() from the std::exception.

furthermore, i know that it is unsafe to create a string in my exception handler, but i'm not an expert on this topic, so what are some safer alternatives?

struct Exception : public std::exception, private boost::noncopyable
{
    public:
        Exception(std::string msg)
            : message(msg)
        {}
        ~Exception()
        {}

        virtual const char* what() const throw 
        {
            std::string what = message + // and now what? base.what()
            LOG(what); // write to log file
            return what.c_str();
        }

    private:
        std::string message;
};

EDIT: i really have asked my question the wrong way. i'm rather interested in safety, i just thought it'd be nice to have more data for logging. i was wrong.

now, i'm not being so paranoid about bad_alloc being thrown by the message string in case there was a bad_alloc before, i'd rather have a neat message. that being said i rewrote some stuff:

struct Exception : public std::exception
{
    public:
        Exception(std::string msg)
            : message(msg)
        {}
        ~Exception()
        {}

        virtual const char* what() const throw 
        {
            LOG(what); // write to log file
            return what.c_str();
        }

    private:
        std::string message;
};

are there still any big concerns about that code now? the LOG() throws std::exception i case something goes wrong, because i didn't want an infinite loop of log calling by derived exception class, and that class again calling log which would cause the same exeception again. Will this work like i want it to, or will a logging exception in my derived class call terminate() or cause core dump?

like image 211
cppanda Avatar asked Jan 31 '11 23:01

cppanda


People also ask

What are std :: exceptions?

std::exceptionProvides consistent interface to handle errors through the throw expression. All exceptions generated by the standard library inherit from std::exception.

What must a class inherit from in order to be used to throw an exception?

For an objects to be throwable (e.g., throw new ...), it must be constructed from some class in the Throwable hierarchy (either Throwable or one of its subclasses).

How do you throw a custom exception in C++?

In the main() function, within the try block, an object of MyCustomException is created with a string value passed for the message parameter. The exception is then thrown using the throw keyword and caught in the catch block. The message is then printed by accessing the what() function.


3 Answers

EDIT: Since writing this answer, I've stumbled upon the Error and Exception Handling section in the Boost document. I would recommend that document over this answer.


First off, making your exception not-copyable is a bad idea. When you write something such as

// could be any exception, doesn't matter.
throw Exception(...);

The runtime creates a copy of that object into a special location. Some compilers might optimize this and create the original object in that location, but the The C++ Programming Language says it's a copy, and I also believe that's what the standard says, though I'm not sure. You might get away with this in your current environment, but that might not always be the case.

Then, everything else depends on how paranoid you are with corner cases.

The memory allocation part is mostly flaky in the exception clause (i.e. the constructor). If this memory allocation happens to fail (i.e. std::bad_alloc is thrown), there are two possibilities, depending on how you write your throw statement:

  1. std::string is created before the throw statement, std::bad_alloc replaces the exception you thought you would raise, problem is sort-of badly reported.
  2. std::string is created inline in the constructor call. If this is considered "during exception handling" by the standard, std::unexpected()/std::terminate() will be invoked and you basically get a core dump.

In any case, it seems like you won't get the desired effect of reporting your error.

I always recommend to create some sort of temporary state that doesn't allocate memory in the constructor and wait for the call to std::what() to create the string that reports the error, but that might still lead to case #1. You could resort to some compile-time determined buffer size to make sure that doesn't happen.

Many people will tell you they've never had problems with allocating strings in constructors because it's unlikely std::bad_alloc will be raised unless the original exception was std::bad_alloc in the first place. Hence, it depends on your level of paranoia.

like image 170
André Caron Avatar answered Sep 29 '22 11:09

André Caron


I won't go in to the myriad problems there are with your code because it's a huge can of worms. But here is how you call a base class method:

std::exception::what()
like image 27
John Dibling Avatar answered Sep 29 '22 11:09

John Dibling


The answer to this question is, unless you've explicitly set the value that what() should return from std::exception then you don't want to call it. Fact of the matter is that it's going to behave in ways you might not expect and dissimilarly on different implementations.

Note that the standard provides no functionality in std::exception to provide the string value to begin with. Those implementations that actually provide useful information from calling std::exception::what() add additional, non-standard functionality to the class. For example, MSVC has an exception(char const* const&) constructor. This isn't in the standard and you probably don't want to depend upon it.

Your better bet is to never call std::exception::what from a derived class. Sure, do upcalls in your custom versions that subclass things UNDER std::exception, but don't do it in direct derivatives.

If you do insist on calling this function directly then you'd damn well better check for NULL because that's what you're probably going to get.

like image 27
Edward Strange Avatar answered Sep 29 '22 10:09

Edward Strange