开发者

Catch Multiple Custom Exceptions? - C++

I'm a student in my first C++ programming class, and I'm working on a project where we have to create multiple custom exception classes, and then in one of our event handlers, use a try/catch block to handle them appropriately.

My question is: How do I catch my multiple custom exceptions in my try/catch block? GetMessage() is a custom method in my exception classes that returns the exception explanation as a std::string. Below I've included all the relevant code from my project.

Thanks for your help!

try/catch block


    // This is in one of my event handlers, newEnd is a wxTextCtrl
try {
    first.ValidateData();
    newEndT = first.ComputeEndTime();
    *newEnd << newEndT;
}
catch (// don't know what do to here) {
    wxMessageBox(_(e.GetMessage()), 
                 _("Something Went Wrong!"),
                 wxOK | wxICON_INFORMATION, this);;
}

ValidateData() Method


void Time::ValidateData()
{
    int startHours, startMins, endHours, endMins;

    startHours = startTime / MINUTES_TO_HOURS;
    startMins = startTime % MINUTES_TO_HOURS;
    endHours = endTime / MINUTES_TO_HOURS;
    endMins = endTime % MINUTES_TO_HOURS;

    if (!(startHours <= HOURS_MAX && startHours >= HOURS_MIN))
        throw new HourOutOfRangeException("Beginning Time Hour Out of Range!");
    if (!(endHours <= HOURS_MAX && endHours >= HOURS_MIN))
        throw new HourOutOfRangeException("Ending Time Hour Out of Range!");
    if (!(startMins <= MINUTE_MAX && startMins >= MINUTE_MIN))
        throw new MinuteOutOfRangeException("Starting Time Minute Out of    Range!");
    if (!(endMins <= MINUTE_MAX && endMins >= MINUTE_MIN))
        throw new MinuteOutOfRangeException("Ending Time Minute Out of Range!");
    if(!(timeDifference <= P_MAX && timeDifference >= P_MIN))
        throw new PercentageOutOfRangeException("Percentage Change Out of Range!");
    if (!(startTime < endTime))
        throw new StartEndException("Start Time Cannot Be Less Than End Time!");
}

Just one of my custom exception classes, the others have the same structure as this one


class HourOutOfRangeException
{
public:
        // param constructor
        // initializes message to passed paramater
        // preconditions - param will be a string
        // postconditions - message will be initialized
        // params a string
        // no return type
        HourOutOfRangeException(string pMessage) : message(pMessage) {}
        // GetMessage is getter for开发者_开发知识库 var message
        // params none
        // preconditions - none
        // postconditions - none
        // returns string
        string GetMessage() { return message; }
        // destructor
        ~HourOutOfRangeException() {}
private:
        string message;
};


If you have multiple exception types, and assuming there's a hierarchy of exceptions (and all derived publicly from some subclass of std::exception,) start from the most specific and continue to more general:

try
{
    // throws something
}
catch ( const MostSpecificException& e )
{
    // handle custom exception
}
catch ( const LessSpecificException& e )
{
    // handle custom exception
}
catch ( const std::exception& e )
{
    // standard exceptions
}
catch ( ... )
{
    // everything else
}

On the other hand, if you are interested in just the error message - throw same exception, say std::runtime_error with different messages, and then catch that:

try
{
    // code throws some subclass of std::exception
}
catch ( const std::exception& e )
{
    std::cerr << "ERROR: " << e.what() << std::endl;
}

Also remember - throw by value, catch by [const] reference.


You should create a base exception class and have all of your specific exceptions derive from it:

class BaseException { };
class HourOutOfRangeException : public BaseException { };
class MinuteOutOfRangeException : public BaseException { };

You can then catch all of them in a single catch block:

catch (const BaseException& e) { }

If you want to be able to call GetMessage, you'll need to either:

  • place that logic into BaseException, or
  • make GetMessage a virtual member function in BaseException and override it in each of the derived exception classes.

You might also consider having your exceptions derive from one of the standard library exceptions, like std::runtime_error and use the idiomatic what() member function instead of GetMessage().


When templates can't, macros save the day. The solution is taken from Boost. It boils to 7 lines of code.

/// @file multicatch.hpp
#include <boost/preprocessor/variadic/to_list.hpp>
#include <boost/preprocessor/list/for_each.hpp>

/// Callers must define CATCH_BODY(err) to handle the error,
/// they can redefine the CATCH itself, but it is not as convenient. 
#define CATCH(R, _, T) \
  catch (T & err) {    \
    CATCH_BODY(err)    \
  }
/// Generates catches for multiple exception types
/// with the same error handling body.
#define MULTICATCH(...) \
  BOOST_PP_LIST_FOR_EACH(CATCH, _, BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__))
// end of file multicatch.hpp

/// @file app.cc
#include "multicatch.hpp"

// Contrived example.
/// Supply the error handling logic.
#define CATCH_BODY(err)                        \
  log() << "External failure: " << err.what(); \
  throw;

void foo() {
  try {
    bar();  // May throw three or more sibling or unrelated exceptions.
  }
  MULTICATCH(IOError, OutOfMemory)
}

#undef CATCH_BODY


Another way to solve this problem when you don't have control on the class hierarchy of the exceptions and that you can't duplicate the contents of the catch block is using dynamic_cast Like this:

try
{
   ...
}
catch (std::exception& e)
{
    if(   nullptr == dynamic_cast<exception_type_1*> (&e)
       && nullptr == dynamic_cast<exception_type_2*> (&e))
    {
        throw;
    }
    // here you process the expected exception types
}


Derive all of your exceptions from a common base class BaseException that has a virtual method GetMessage().

Then catch(const BaseException& e).


I had a similar problem today, but it turned out I didn't need my solution to solve my problem. Honestly, I couldn't think of real use cases (logging?), and I didn't find much use for it in my code.

Anyway, this is an approach with type lists (requires C++11). I think the advantage of this approach is that there's no need to have a common base class for custom exceptions (except for std::exception, maybe?). In other words, it is not intrusive to your exception hierarchy.

There might be some subtle errors that I am not aware of.

#include <type_traits>
#include <exception>

/// Helper class to handle multiple specific exception types
/// in cases when inheritance based approach would catch exceptions
/// that are not meant to be caught.
///
/// If the body of exception handling code is the same
/// for several exceptions,
/// these exceptions can be joined into one catch.
///
/// Only message data of the caught exception is provided.
///
/// @tparam T  Exception types.
/// @tparam Ts  At least one more exception type is required.
template <class T, class... Ts>
class MultiCatch;

/// Terminal case that holds the message.
/// ``void`` needs to be given as terminal explicitly.
template <>
class MultiCatch<void> {
 protected:
  explicit MultiCatch(const char* err_msg) : msg(err_msg) {}
  const char* msg;
};

template <class T, class... Ts>
class MultiCatch : public MultiCatch<Ts...> {
  static_assert(std::is_base_of<std::exception, T>::value, "Not an exception");

 public:
  using MultiCatch<Ts...>::MultiCatch;

  /// Implicit conversion from the guest exception.
  MultiCatch(const T& error) : MultiCatch<Ts...>(error.what()) {}  // NOLINT

  /// @returns The message of the original exception.
  const char* what() const noexcept {
    return MultiCatch<void>::msg;
  }
};

/// To avoid explicit ``void`` in the type list.
template <class... Ts>
using OneOf = MultiCatch<Ts..., void>;

/// Contrived example.
void foo() {
  try {
    bar();  // May throw three or more sibling or unrelated exceptions.
  } catch (const OneOf<IOError, OutOfMemory>& err) {
    log() << "External failure: " << err.what();

    throw;  // Throw the original exception.
  }
}


I run into the same problem and here is what I ended up with:

  std::shared_ptr<MappedImage> MappedImage::get(const std::string & image_dir,
                                                const std::string & name,
                                                const Packet::Checksum & checksum) {
    try {
      return std::shared_ptr<MappedImage>(images_.at(checksum));
    } catch (std::out_of_range) {
    } catch (std::bad_weak_ptr) {
    }
    std::shared_ptr<MappedImage> img =
      std::make_shared<MappedImage>(image_dir, name, checksum);
    images_[checksum_] = img;
    return img;
  }

In my case the function returns when it doesn't get an exception. So I don't actually have to do anything inside the catch but can do the work outside the try.


#include <iostream> void test(int x)` { try{ if(x==1) throw (1); else if(x==2) throw (2.0); } catch(int a) { cout<<"It's Integer"; } catch(double b) { cout<<"it's Double"; } } int main(){ cout<<" x=1"; test(1); cout<<"X=2"; test(2.0); return 0; }`
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜