0

This question is similar to c++ Exception Class Design and follows:

I want to design exception class hierarchy for my application and here are the design points I used:

  1. Exception should be derived from standard exception classes (those are std::exception, std::logic_error and std::runtime_error).

  2. Exception class should be able to take error description (i.e. what is called what) and position where it occurred (const std::string &file, int line)

  3. Exception should not throw any exception during construction or from any other member.

Given this I have:

#define throw_line(TException, what) throw TException((what), __FILE__, __LINE__)

class AnException : public std::exception {
public:
    AnException(const std::string &what, const std::string &file, int line) noexcept {
        try {
            what_ = what;
            file_ = file;
            line_ = line;
        } catch (std::exception &e) {
            was_exception_ = true;
        }
    }
    virtual ~AnException() noexcept {}

    virtual const char *what() const noexcept override {
        if (was_exception_) {
            return "Exception occurred while construct this exception. No further information is available."
        } else {
            try {
                std::string message = what_ + " at " + file_ + ":" + std::to_string(line);
                return message.c_str();
            } catch (std::exception &e) {
                return "Exception occurred while construct this exception. No further information is available."
            }
        }
    }
};

class ParticularException : public AnException {
    ...
}

As you can see, it appears to be somewhat complex to construct such class, because we definitely should not have exceptions in constructor (otherwise std::terminate() will be called) or in what() member.

The question: is this example of a good design or I should remove some restrictions (like, having file/line information) to simplify it? Is there a better way?

I'm free to use C++11/C++14, but trying to keep off C++17 since it is not yet finished and compilers may not fully implement it.

Note: I want this code to be cross-platform.

Thanks in advance.

Edit 1: The follow up question: how can I retire file/line information, but keep that in logs? May be printing stacktrace is a better solution than what I have now? I mean leave exception class which just holds an error message (what) and call something like print_backtrace on upper level of exception handling chain.

Community
  • 1
  • 1
maverik
  • 5,508
  • 3
  • 35
  • 55
  • 1
    __FILE__ usually expands to a literal. This means that you should be able to use... template constructor(const char (&array)[N])... ...which could prevent the exception. You could force what to be a literal to in similar fashion. – Werner Erasmus Jan 14 '16 at 18:17
  • 2
    See http://stackoverflow.com/a/24520735/576911 for how to make your exception copy constructor noexcept. – Howard Hinnant Jan 14 '16 at 20:40

2 Answers2

1

Relating to my comment, depending on whether a what literal is acceptable, I had something like this in mind:

#include <array>
template <int N> constexpr std::size_t arraySize(const char (&)[N]){return N;}

template <class ExceptT, std::size_t whatN, std::size_t fileN>
  class MyExcept : public ExceptT
{
  static_assert(std::is_base_of<std::exception, ExceptT>::value, "bad not base");

  public:
    MyExcept(
      const char (&what)[whatN],
      const char (&file)[fileN],
      int line) noexcept
    : ExceptT(""), //Using our own what
      what_(), file_(), line_(line)
    {
      std::copy(std::begin(what), std::end(what), begin(what_));
      std::copy(std::begin(file), std::end(file), begin(file_));
    }

    virtual const char *what() const noexcept override
    {
      //....
    }

  private:
    std::array<char,whatN> what_;
    std::array<char,fileN> file_;
    int line_;
};

#define throw_line(TException, what) throw MyExcept<TException,arraySize(what),arraySize(__FILE__)>(what,__FILE__, __LINE__)

void driver()
{
  throw_line(std::runtime_error, "Hoo hah");
}

I've added some code that allows deriving from a std::exception type (type requires constructor with single literal argument (could check that this is also noexcept). I'm passing it an empty string literal, so std::exception class should at least not throw. I'm using static_assert to check this.

  • it cannot throw on construction...
  • it derives from std::exception...
  • it contains "fixed" what and position
Werner Erasmus
  • 3,988
  • 17
  • 31
0

This usually happens only, if you want to catch certain errors in another catch-block than your most outer catch block. I would only start doing this, if I see a demand. I remember that my system-call-read() wrapper would throw a different exception for EOF -- since this sometimes need to be caught. Then I've got a system-error-exception and a normal message exception, since std::exception does not store any message with certain compilers.

Separate classes for exceptions are overused in the same way throw-specifications were overused.

Please remember that if you want to catch a certain error somewhere else than in the outer-most-catch-block, you need to be able to do something about the error, other than re-throw it. Usually the string returned from what() should already be sufficient to tell you what kind of error occurred. Thus for printing reasons there is no need to overload the exception type.