Search code examples
c++boostcustom-exceptions

Using C++ Boost's format object as an exception member


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?


Solution

    • 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.