1

I´m moving from C# to C++ so forgive me if the question is basic or has some misconceptions...

I want to build my own exception to be used on my try/catch blocks. I need to report a custom exception code, a custom exception message and a custom exception source origin - I may have all or some of these parameters.

So I´ve built that class:

CommonException.hpp

namespace exceptionhelper
{

    class CommonException : public std::exception {

    public:
        CommonException();
        CommonException(std::string message);
        CommonException(std::string source, std::string message);
        CommonException(int code, std::string source, std::string message);
        virtual ~CommonException();
        const char *what() const throw();


    private:
        int exceptionCode;
        std::string exceptionSource;
        std::string exceptionMessage;
    };

}

And the implementation:

CommonException.cpp

namespace exceptionhelper {
        CommonException::CommonException() {
            exceptionCode = 0;
            exceptionMessage = "No message.";
            exceptionSource = "No source.";
        }
        CommonException::CommonException(std::string message) {
            exceptionCode = 0;
            exceptionMessage = message;
            exceptionSource = "No source.";
        }
        CommonException::CommonException(std::string source, std::string message) {
            exceptionCode = 0;
            exceptionMessage = message;
            exceptionSource = source;
        }
        CommonException::CommonException(int code, std::string source, std::string message) {
            exceptionCode = code;
            exceptionMessage = message;
            exceptionSource = source;
        }
        CommonException::~CommonException() {
        }
        const char *CommonException::what() const throw()
        {
            std::stringstream s;
            s << "Code    : " << exceptionCode << std::endl;
            s << "Source  : " << exceptionSource << std::endl;
            s << "Message : " << exceptionMessage << std::endl;
            return s.str().c_str();
        }
    }

And finally my implementation:

main ()
{
   try {
        ... code ...

        throw new CommonException(10, "here", "test exception");

   }
   catch (const std::exception &exc)
   {
            std::cerr << "Exception detected:" << std::endl;
            std::cerr << exc.what();
        }
        catch (...)
        {
            std::cerr << "Unknown exception called." << std::endl;
            throw;
        }
}

For some reason I´m getting this result:

Unknown exception called.
terminate called after throwing an instance of 'linuxcommon::exceptionhelper::CommonException*'
Aborted (core dumped)

Questions:

a) Why am I not catching my custom exception ? b) I´m pretty sure there are better ways to do this exception handling, but I cannot figure that out yet...

Thanks for helping...

Mendes
  • 17,489
  • 35
  • 150
  • 263
  • 4
    `new` returns a pointer. You don't need to use it here. You hardly ever need to call `new` in C++. – juanchopanza Apr 27 '15 at 22:01
  • 2
    I.e. you're throwing a `CommonException *` and catching a `std::exception &`. One is a pointer, the other a reference. Climb out of the C# box and drink from the C++ fountain. Lose the `new`. Then fix the string problem Alex describes below. – WhozCraig Apr 27 '15 at 22:06
  • Well, well... I´m relly drinking from the C++ fountain, but my body seens to be refusing it... Removing the new solved the problem! Thanks for helping! – Mendes Apr 27 '15 at 22:17
  • 1
    `return s.str().c_str();` is not right. You're returning a buffer associated with a temporary variable associated with a local stream. All of them are going away before the function returns. This error is called "RETURNING A DANGLING POINTER". – Ben Voigt Apr 27 '15 at 22:23

1 Answers1

4

Some notes for your code.

  1. You may want to derive your exception class from std::runtime_error (instead of std::exception), since std::runtime_error already provides a constructor with an error message string. Of course you can add your own exception's data members to the derived class.

  2. You don't need to define a virtual destructor with an empty body for your exception class, since you don't have explicit cleanup code to execute. std::exception has a virtual destructor, and that will work just fine for your derived exception class.

  3. You can use a more idiomatic C++ syntax to initialize your exception data members in constructors, e.g. instead of:

CommonException::CommonException() {
    exceptionCode = 0;
    exceptionMessage = "No message.";
    exceptionSource = "No source.";
}

you can use:

CommonException::CommonException()
  : exceptionCode(0), 
    exceptionMessage("No message."), 
    exceptionSource("No source.")
{ }
  1. If you pass string parameters, you can still pass by value, but you may want to std::move() from the value to initialize data members, e.g. instead of:
CommonException::CommonException(std::string source, std::string message) {
    exceptionCode = 0;
    exceptionMessage = message;
    exceptionSource = source;
}

You can do:

CommonException::CommonException(std::string source, std::string message) 
    : exceptionCode(0),
      exceptionMessage(std::move(message)),
      exceptionSource(std::move(source))
{
}
  1. Consider making your single-string-argument constructor explicit, to prevent bogus implicit conversions from strings to exceptions:

    explicit CommonException(std::string message);
    
  2. In its current form, your what() method implementation can throw, because inserting operations (<<) on std::stringstream can throw:

const char *CommonException::what() const throw()
{
    std::stringstream s;
    s << "Code    : " + exceptionCode << std::endl;
    s << "Source  : " + exceptionSource << std::endl;
    s << "Message : " + exceptionMessage << std::endl;
    return s.str().c_str();
}

So, remove the throw() specification, making it simply:

const char* CommonException::what() const

(As a side note, the modern C++11 way of marking a method as non-throwing is using noexcept).

You may also want to simply use '\n' instead of std::endl to avoid premature pessimization.

Moreover, you are returning in this line a temporary string:

 return s.str().c_str();

The const char* pointer returned to the caller will just point to some garbage at the call site: this introduces a bug.

To fix that, you may want to consider adding a std::string data member, format the whole error message string inside that data member during exception object construction (i.e. in your constructors - you may also build a private helper method to do that, to avoid repeating code in each constructor), and just return m_str.c_str() from the what() method.

If you derive your exception class from std::runtime_error, you can just build the whole error message string at construction time, and pass that to std::runtime_error's constructor. In this case, std::runtime_error::what() will Do The Right Thing, and you won't need to override what() in your exception class.

E.g.

// Derive from std::runtime_error instead of std::exception
class CommonException : public std::runtime_error

...

CommonException::CommonException( /* your exception data */ )
    : std::runtime_error(BuildErrorMessage( /* your exception data */ )
{ }

// Private helper method
std::string CommonException::BuildErrorMessage( /* your exception data */ )
{
    // Build your exception message string here,
    // e.g. you can use std::ostringstream here,
    // and just call its str() method
    // to return the whole error message string.
    ...
}

At the exception's "client" side, you have:

  ... code ...

    throw new CommonException(10, "here", "test exception");

}
catch (const std::exception &exc)

Instead, consider throwing by value, instead of dynamically allocating the exception object on the heap, e.g. simply do:

throw CommonException( /* your exception data*/ );
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • i think taking in const ref to string ctor would be better – Sarang Apr 27 '15 at 22:25
  • @Sarang: In general, if you are _observing_ an object that is not cheap to copy (like a `std::string`), using const reference would be fine. But in case of making a **local copy** (e.g. in a data member), passing by value and moving from the value is a better approach, considering C++11 move semantics. – Mr.C64 Apr 27 '15 at 22:37
  • Mr.C64, why the `std::move` ? – Mendes Apr 27 '15 at 22:40
  • @Mendez: You may want to search about techniques involving C++11 parameter passing. [This thread on StackOverflow](http://stackoverflow.com/q/10472179/1629821) is a good starting point. – Mr.C64 Apr 27 '15 at 22:53
  • All fine, but the code does not allow me to remove the `throw()` on `what`... error: `looser throw specifier for virtual const char* CommonException::what() const` – Mendes Apr 27 '15 at 23:05
  • 1
    If you follow my suggestion and derive your exception class from `std::runtime_error()`, you don't need to override and reimplement `what()`. You should also remove `throw()` from both the declaration of the method (in the header file) and from the definition (in the .cpp file). Moreover, I think if you really want to override `what()`, assuming your error message was formatted in a `std::string m_errorMsg` data member, `return m_errorMsg.c_str();` from `what()` should be fine. – Mr.C64 Apr 27 '15 at 23:27
  • @Mr.C64: how are the two approaches different? The second approach seems to be using the new move semantics- but otherwise both create a copy (either you are calling a copy ctor or move ctor) – Sarang Apr 27 '15 at 23:57