James Franco
James Franco

Reputation: 4706

Exception class with a char* constructor

I came across the following code on VS2008

if (!CreateProcess( NULL,
                    const_cast<LPWSTR>(ss.str().c_str()),
                    NULL,
                    NULL,
                    FALSE,
                    CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS,
                    NULL,
                    NULL,
                    &si,
                    &pi))
{
    throw   std::exception("Unable to format Device");
}

Now I am porting the code to mingw gcc and I get the error

error: no matching function for call to 'std::exception::exception(const char [23])'

Investigating the issue I noticed that Visual Studio has a file exception which does have an exception class and does take in char*. Some of the definitions look like this

   __CLR_OR_THIS_CALL exception();
    __CLR_OR_THIS_CALL exception(const char *const&);
    __CLR_OR_THIS_CALL exception(const char *const&, int);
    __CLR_OR_THIS_CALL exception(const exception&);
    exception& __CLR_OR_THIS_CALL operator=(const exception&);
    virtual __CLR_OR_THIS_CALL ~exception();
    virtual const char * __CLR_OR_THIS_CALL what() const;

My question is how should I circumvent this build issue on mingw gcc ? Should I create a new class that inherits from std::runtime_error and throw that instead ?

Upvotes: 34

Views: 24762

Answers (1)

Wintermute
Wintermute

Reputation: 44023

Opinion plays a role here. The problem is that std::exception does not have a constructor that takes a string argument; this is an MSVC extension. I see two ways to go about it:

  1. Don't pass a string argument
  2. Don't use std::exception

The first case is straightforward; just use

throw std::exception();

The drawback is that you don't get a descriptive error message.

If the error message is important, using std::exception directly is not an option. In this case, you could use either std::logic_error or std::runtime_error, which inherit std::exception and do have constructors taking a string argument, so

throw std::runtime_error("Unable to format Device");

might already solve the problem. catch clauses that caught the std::exception will also catch the std::runtime_error. There is one potential problem, though: catch clauses that catch std::runtime_error would not have caught the std::exception but will catch this one.

This seems like a bit of a corner case, and it is entirely possible that it is not a problem for you. If, however, there is a possibility that along the call stack there is a catch clause that catches std::runtime_error but should not catch the exception thrown by this code, you could derive your own exception class from std::exception that does take a string argument. Because the class is new, it will not be caught by existing catch clauses. For example:

class descriptive_exception : public std::exception {
public:
  descriptive_exception(std::string const &message) : msg_(message) { }
  virtual char const *what() const noexcept { return msg_.c_str(); }

private:
  std::string msg_;
}

And then

throw descriptive_exception("Unable to format Device");

This is arguably not very pretty, and it is unlikely that it is necessary, so the more probable solution is to use std::runtime_error or std::logic_error (or a class derived from one of them).

Whether std::logic_error or std::runtime_error is more appropriate is not very clear-cut; in this case I'd probably go with std::runtime_error because the error does not seem even theoretically predictable, but given that std::domain_error and std::future_error derive from std::logic_error, it would not be entirely out of place in that hierarchy. This is, I think, a matter of opinion.

Upvotes: 82

Related Questions