Je suis étudiant dans ma première classe de programmation C++ et je travaille sur un projet dans lequel nous devons créer plusieurs classes d'exceptions personnalisées, puis dans l'un de nos gestionnaires d'événements, utilisez un bloc try/catch
pour les gérer correctement.
Ma question est la suivante: comment intercepter mes multiples exceptions personnalisées dans mon bloc try/catch
? GetMessage()
est une méthode personnalisée dans mes classes d'exceptions qui renvoie l'explication de l'exception sous la forme d'un std::string
. Ci-dessous, j'ai inclus tout le code pertinent de mon projet.
Merci de votre aide!
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);;
}
Méthode ValidateData ()
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!");
}
Juste une de mes classes d'exception personnalisées, les autres ont la même structure que celle-ci
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;
};
Si vous avez plusieurs types d'exceptions, et en supposant qu'il existe une hiérarchie d'exceptions (toutes dérivées publiquement d'une sous-classe de std::exception
,), commencez par le plus spécifique et continuez de manière plus générale:
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
}
D'autre part, si vous ne vous intéressez qu'au message d'erreur - throw
même exception, dites std::runtime_error
avec des messages différents, puis catch
qui:
try
{
// code throws some subclass of std::exception
}
catch ( const std::exception& e )
{
std::cerr << "ERROR: " << e.what() << std::endl;
}
Rappelez-vous également - projection par valeur, capture par référence [const].
Vous devez créer une classe d'exception de base et faire en sorte que toutes vos exceptions spécifiques en dérivent:
class BaseException { };
class HourOutOfRangeException : public BaseException { };
class MinuteOutOfRangeException : public BaseException { };
Vous pouvez ensuite toutes les attraper dans un seul bloc:
catch (const BaseException& e) { }
Si vous voulez pouvoir appeler GetMessage
, vous devez soit:
BaseException
, ouGetMessage
une fonction membre virtuelle dans BaseException
et remplacez-la dans chacune des classes d'exceptions dérivées.Vous pouvez également envisager que vos exceptions dérivent de l'une des exceptions standard de la bibliothèque, telle que std::runtime_error
, et utilisez la fonction membre idiomatique what()
au lieu de GetMessage()
.
Dérivez toutes vos exceptions d'une classe de base commune BaseException
dotée d'une méthode virtuelle GetMessage()
.
Puis catch(const BaseException& e)
.
Je rencontre le même problème et voici ce que j'ai fini avec:
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;
}
Dans mon cas, la fonction retourne quand il n'y a pas d'exception. Je n'ai donc rien à faire à l'intérieur de la prise, mais je peux faire le travail en dehors de l'essai.
Lorsque les modèles ne peuvent pas, les macros sauvent la journée… .. La solution provient de Boost . Il se résume à 7 lignes de 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
J'ai eu un problème similaire aujourd'hui, mais il s'est avéré que je n'avais pas besoin de ma solution pour résoudre mon problème. Honnêtement, je ne pouvais pas penser à de vrais cas d'utilisation (journalisation?), Et je n'en trouvais pas beaucoup d'utilisation dans mon code.
Quoi qu’il en soit, il s’agit d’une approche avec des listes de types (nécessite C++ 11). Je pense que l'avantage de cette approche est qu'il n'est pas nécessaire d'avoir une classe de base commune pour les exceptions personnalisées (sauf pour std :: exception, peut-être?). En d'autres termes, cela n'est pas intrusif pour votre hiérarchie d'exceptions.
Il pourrait y avoir des erreurs subtiles dont je ne suis pas au courant.
#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.
}
}