0

I'm trying to write an exception class that needs to be thrown when a system call fails. The exception should have a developer message and the errno code, and it's what method should format the developer message along with the error code. The C way to do the formatting is snprintf, but I'm trying to avoid that. I tried defining a class member for the exception of type std::stringstream. However, that did not work since stringstream has a private copy constructor. Looking for an alternative I learned about Boost's format object. I tried using it and got a different error:

In file included from tun_device.cc:7:0:
system_error.h:9:7: error: looser throw specifier for ‘virtual SystemError::~SystemError()’
 class SystemError : public exception
       ^
In file included from system_error.h:4:0,
                 from tun_device.cc:7:
/usr/include/c++/4.8/exception:64:13: error:   overriding ‘virtual std::exception::~exception() throw ()’
     virtual ~exception() _GLIBCXX_USE_NOEXCEPT;

The way to solve this was to define my own destructor:

~SystemError() throw() {

}

As I understand, this line specifies that the destructor of this exception should not throw any exceptions.

Here's the complete class:

class SystemError : public exception
{
public:
    int m_errno;
    const char * m_message;

    SystemError(int err, const char * message) :
            fmt("%1%: %2%") {
        fmt % message % errno;
        m_errno = err;
        this->m_message = message;
    }

    const char * what() const throw(){
        return fmt.str().c_str();
    }

    ~SystemError()  throw() {

    }

private:
    format fmt;
};

I have several questions:

  1. First of all - Am I reinventing the wheel? Is already there a recommended C++ way to handle failed system calls?

  2. Why does using the format class as a member of the exception forces me to override the default destructor?

  3. Is there any pitfall I should be aware of now that I added my own destructor?

  4. Is there a way to achieve what I want using only the standard C++ library?

reish
  • 831
  • 7
  • 18
  • 1
    I would make `fmt` static, since it can be shared by all instances of `SystemError` and do the formatting within `what()`. – Khouri Giordano Jul 21 '14 at 06:00
  • don't add more per-instance to an exception than absolutely necessary. a formatter is not necessary. – Cheers and hth. - Alf Jul 21 '14 at 06:04
  • @KhouriGiordano - That's an interesting idea. If I understand correctly then it's not thread safe. Am I wrong? – reish Jul 21 '14 at 06:26
  • Parsing the string passed to the format ctor takes time, but making a local copy of a static will be fast and thread-safe. Now that I think about it, I wouldn't do any formatting within the exception itself, only for when it is presented to the user via console, log file, etc. – Khouri Giordano Jul 21 '14 at 07:25
  • Khouri are you seriously talking about prematurely optimizing exception handling? – sehe Jul 21 '14 at 10:51

1 Answers1

3
  • Exception can be handled using the standard exception class or creating your own classes derived from std::exception class. Inheritance would be used when you would want to extend the already available functionality available using std::exception. So the decision is totally dependent on how you want design your application.

  • boost::format does not force you to override the destructor. If you notice you are deriving from std::exception and composing boost::format. std::exception destructor is declared as virtual and has a no throw nature.

    virtual ~exception() throw();

When you leave out the destructor, compiler implicitly provides a destructor, but this destructor does not have no throw() nature, hence compile error:

format.cpp:5: error: looser throw specifier for âvirtual SystemError::~SystemError()â
/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/exception:63: error:   overriding âvirtual std::exception::~exception() throw ()â

When you provide a constructor but as:

   ~SystemError();    //since the base class std::exception has a destrutor with no throw, the child should also follow the same, and again in this case compile time error is received:


      format.cpp:25: error: looser throw specifier for âvirtual SystemError::~SystemError()â
    /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/exception:63: error:   overriding âvirtual std::exception::~exception() throw ()â

To resolve the error SystemError should define a destructor like this:

~SystemeError() throw();

Sample SSCCE code:

  #include <iostream>
#include<boost/format.hpp>

class SystemError : public std::exception
{
  public:
    int m_errno;
    const char * m_message;

    SystemError(int err, const char * message) :
      fmt("%1%: %2%") {
        fmt % message % err;
        m_errno = err;
        this->m_message = message;
      }

    const char * what() const throw(){
      return fmt.str().c_str();
    }

    ~SystemError()  throw() {

    }

    //    ~SystemError()  {
    //
    //    }


  private:
    boost::format fmt;
};


int main(){

return 0;
}
  • Adding destructor is like taking responsibilities of your actions. Destructor could be used to clear any heap memory assigned in the class. Absence of destructor would lead compiler to provide an implicit destructor, which could be cause of issues in specific cases.

    The compilation error will not be received if the class in discussion has primitive data types.

Nik
  • 1,294
  • 10
  • 16
  • So if I understand correctly, without the `format` member the compiler does not generate an destructor for my exception class, since the other members are not classes and therefore don't need to be destructed. Once I add the first member which is also a class, the compiler auto-generates a destructor for my exception, even if I don't define it myself. The compiler auto generated destructor doesn't have a "no throw" nature, in contrary to it's ancestor destructor. Am I correct? – reish Jul 21 '14 at 06:35
  • 1
    Yes your understanding is correct. When your class has some members(boost::format or std::string) which require cleanup, compiler gives error if correct destructor is not provided. But if you have primitive data types then you will not get the error. The destructor with POD types or no data members becomes Trivial ( ref http://en.cppreference.com/w/cpp/language/destructor) – Nik Jul 21 '14 at 06:53