87

I've just created exception hierarchy and wanted to pass char* to constructor of one of my derived classes with a message telling what's wrong, but apparently std::exception doesn't have constructor which would allow me to do so. Yet there is a class member called what() which would suggest that some information can be passed.
How can I (can I?) pass text to derived class of a std::exception in order to pass info with my exception class, so I can say somewhere in the code:

throw My_Exception("Something bad happened.");
kmiklas
  • 13,085
  • 22
  • 67
  • 103
smallB
  • 16,662
  • 33
  • 107
  • 151
  • I know this doesn't answer your question, but you might wanna read [this](http://www.codeproject.com/KB/cpp/cppexceptionsproetcontra.aspx) before you start using exceptions. There are also many questions here on stack overflow about exceptions being good or bad (the answer mostly being bad). – Shahbaz Nov 16 '11 at 13:56
  • https://stackoverflow.com/q/1669514/52074 is extremely similar (almost seems like duplicate) and has more upvotes. FYI both this question and the link have the same solutions. – Trevor Boyd Smith Apr 10 '18 at 12:06
  • This one has a the better answers IMO. Both obmarg's at the top as well as Johannes's alllll the way at the bottom. – hegel5000 Jan 31 '19 at 16:21

6 Answers6

78

If you want to make use of the string constructor, you should inherit from std::runtime_error or std::logic_error which implements a string constructor and implements the std::exception::what method.

Then it's just a case of calling the runtime_error/logic_error constructor from your new inherited class, or if you're using c++11 you can use constructor inheritance.

Frank Kusters
  • 2,544
  • 2
  • 21
  • 30
obmarg
  • 9,369
  • 36
  • 59
75

I use the following class for my exceptions and it works fine:

class Exception: public std::exception
{
public:
    /** Constructor (C strings).
     *  @param message C-style string error message.
     *                 The string contents are copied upon construction.
     *                 Hence, responsibility for deleting the char* lies
     *                 with the caller. 
     */
    explicit Exception(const char* message)
        : msg_(message) {}

    /** Constructor (C++ STL strings).
     *  @param message The error message.
     */
    explicit Exception(const std::string& message)
        : msg_(message) {}

    /** Destructor.
     * Virtual to allow for subclassing.
     */
    virtual ~Exception() noexcept {}

    /** Returns a pointer to the (constant) error description.
     *  @return A pointer to a const char*. The underlying memory
     *          is in posession of the Exception object. Callers must
     *          not attempt to free the memory.
     */
    virtual const char* what() const noexcept {
       return msg_.c_str();
    }

protected:
    /** Error message.
     */
    std::string msg_;
};
OverShifted
  • 457
  • 1
  • 7
  • 17
tune2fs
  • 7,605
  • 5
  • 41
  • 57
  • where did the "msg_" keyword came from? I did not know you can call statement after the ":" of a method declaration. I think this was only for base class. – Nap Jun 10 '15 at 01:41
  • 2
    msg_ is a **protected** member of Exception; it's an instance of std::string, so it has access to it's .c_str member function (converts to c string). –  Jul 05 '15 at 20:11
  • 2
    what about copy constructor? – isnullxbh Mar 12 '17 at 17:38
  • @DDrmmr You should explain why. – Water Nov 04 '17 at 17:01
  • 22
    @Water This code does the same as `std::runtime_error`. No need to reinvent the wheel. – D Drmmr Nov 10 '17 at 12:13
  • 17
    I don't think this is a good idea. Constructing an std::string can throw an exception, which will cause terminate to be called. – Kef Schecter Nov 19 '17 at 20:33
  • 3
    i agree with @DDrmmr's statement that you are reinventing the wheel (i.e. you are re-implementing `std::runtime_error` or `std::logic_error`). – Trevor Boyd Smith Apr 10 '18 at 12:09
  • Your exception does not satisfy requirement of exception design. During construction std::string constructor is called, which may throw an exception. I would say, it's a very unlikely situation, but this code is not well-formed and would not have passed my review. It could be designed in a proper way with a sophisticated class member std::unique_ptr. In this case exception may not thrown from your exception c-tor –  May 18 '18 at 11:24
15

How about this:

class My_Exception : public std::exception
{
public:
virtual char const * what() const { return "Something bad happend."; }
};

Or, create a constructor accepting the description if you like...

  • 2
    @user472155 +1 for the good answer. By the way though, I think it worth to mention here, that the signature you provided in your example for the what function, implies only to code prior to C++11. – Guy Avraham Feb 17 '18 at 18:26
  • 1
    @Guy Avraham: do you mean that post C++11 the virtual keyword should become override, or something else? – gg99 Nov 30 '18 at 11:06
  • 2
    @gg99 - Good point. I meant that since C++11, the signature of the `what` function turned into the following: `virtual const char* what() const noexcept` (note the addition of the `noexcept` keyword at the end). See also here: https://en.cppreference.com/w/cpp/error/exception/what – Guy Avraham Nov 30 '18 at 12:14
13

If your goal is to create an exception so that you do not throw a generic exception (cpp:S112) you may just want to expose the exception you inherit from (C++11) with a using declaration.

Here is a minimal example for that:

#include <exception>
#include <iostream>

struct myException : std::exception
{
    using std::exception::exception;
};

int main(int, char*[])
{
    try
    {
        throw myException{ "Something Happened" };
    }
    catch (myException &e)
    {
        std::cout << e.what() << std::endl;
    }
    return{ 0 };
}

As Kilian points out in the comment section the example depends on a specific implementation of std::exception that offers more constructors than are mentioned here.

In order to avoid that you can use any of the convenience classes predefined in the header <stdexcept>. See these "Exception categories" for inspiration.

Johannes
  • 6,490
  • 10
  • 59
  • 108
  • 4
    Having a look at https://en.cppreference.com/w/cpp/error/exception/exception std::exception does not have a constructor taking a string as argument. So your code is ill-formed. – Kilian Mar 01 '19 at 23:57
  • @Kilian I am using implementation specific behavior which should be common and practical. Thanks for the hint I will include that in the answer. – Johannes Mar 04 '19 at 08:53
7

The what method is virtual, and the meaning is that you should override it to return whatever message you want to return.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

Here is an example

 class CommunicationError: public std::exception {
  public:
   explicit CommunicationError(const char* message) : msg(message) {}
   CommunicationError(CommunicationError const&) noexcept = default;

   CommunicationError& operator=(CommunicationError const&) noexcept = default;
  ~CommunicationError() override = default;

  const char* what() const noexcept override { return msg; }
 private:
  const char* msg;
};

[1] https://www.autosar.org/fileadmin/user_upload/standards/adaptive/17-03/AUTOSAR_RS_CPP14Guidelines.pdf

rjhcnf
  • 762
  • 6
  • 12