2

I have an exception class as follows:

class FileNotFoundException : public std::exception
{
public:
    FileNotFoundException(const char* message) : errorMessage(message){ }
    const char* what() const throw() override
    {
        return this->errorMessage;
    }
private:
    const char* errorMessage;
};

And I throw this exception like this:

std::string message = "Message";
throw ::FileNotFoundException(message.c_str());

But when I try to handle it using:

try
{
    // the code that throws 
}
catch(::FileNotFoundException& ex)
{
    std::string message = ex.what(); 
}

The string is empty. If anyone can help, I would gladly appreciate it.

Thomas Flinkow
  • 4,845
  • 5
  • 29
  • 65

2 Answers2

2

You can't just store a pointer to the message. Try either storing it in std::string, or, better, pass it to the parent constructor. Perhaps it's better to inherit from std::runtime_error in that case.

Here's a complete example:

#include <iostream>
#include <string>
#include <stdexcept>

class FileNotFoundException : public std::runtime_error
{
public:
  FileNotFoundException(const char* message) : std::runtime_error(message)
  {
  }
};

int main()
{
  try {
    throw ::FileNotFoundException("oops, something happened");
  }
  catch(const ::FileNotFoundException& ex) {
    std::cout << "Exception: '" << ex.what() << "'" << std::endl;
  }
}

Compiling and running:

$ g++ -W -Wall --std=gnu++11 a.cpp -oa
$ ./a
Exception: 'oops, something happened'

In short (and without details): The class std::exception doesn't have any constructors. It's just a parent class used by all other exceptions. On the other hand, std::runtime_error has a constructor that stores the message for you properly. A complete explanation can be found in Difference: std::runtime_error vs std::exception()

I think this approach is better than to define what() and using std::string to store the message yourself. That is, if you don't have special needs for the exception class.

You should also check out the C++ exception hierarchy.

Community
  • 1
  • 1
csl
  • 10,937
  • 5
  • 57
  • 89
  • 1
    Thank you, this is the solution. Also thanks for the example, I am going to accept this as the answer as soon as I can. – Thomas Flinkow Apr 11 '17 at 09:36
  • 1
    Your example only fixes it because you pass a real constant string to the exception constructor, instead of having a seperate `std::string` variable on the stack whose pointer is passed. Passing that pointer to the parent constructor doesn't fix the behavior, the string will still not get copied. – Matthias247 Apr 11 '17 at 09:42
  • @Matthias247 I don't know what the standard says about it, but GCC definitely copies the string (I've double checked). Worth looking into. – csl Apr 11 '17 at 09:47
  • 2
    Seems true - I checked here now: http://en.cppreference.com/w/cpp/error/runtime_error `std::runtime_error` seems to copy the constructor argument. Did not expect this. – Matthias247 Apr 11 '17 at 09:49
  • It *may* be that most standard library authors just copy it anyway, because otherwise it would be very dangerous (you're jumping up the stack). So it could be a case where everyone does it, but it could also be the standard doesn't explicitly require it. – csl Apr 11 '17 at 09:50
2

Your problem is here:

throw ::FileNotFoundException(message.c_str());

You are storing a pointer to memory owned by message in the exception. When message goes out of scope (which happens during the throw) the data will no longer be valid. Which means this->errorMessage returns some undefined memory. To fix it you can either pass some really constant string to your exception or you need the exception to own the string, e.g. by making errorMessage a std::string.

Matthias247
  • 9,836
  • 1
  • 20
  • 29