التقاط استثناءات مخصصة متعددة؟ - C ++
-
22-09-2019 - |
سؤال
أنا طالب في أول فصل برمجة C ++ ، وأنا أعمل في مشروع يتعين علينا فيه إنشاء فصول استثناءات مخصصة متعددة ، ثم في أحد معالجات الأحداث لدينا ، استخدم أ try/catch
كتلة للتعامل معها بشكل مناسب.
سؤالي هو: كيف يمكنني التقاط بلدي مضاعف استثناءات مخصصة في بلدي try/catch
منع؟ GetMessage()
هي طريقة مخصصة في فئات الاستثناء الخاصة بي التي تُرجع تفسير الاستثناء كملف std::string
. أدناه قمت بتضمين جميع الكود ذي الصلة من مشروعي.
شكرا لمساعدتك!
حاول/تمسك بلوك
// 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);;
}
طريقة التحقق من صحة ()
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!");
}
واحد فقط من فصول الاستثناءات المخصصة الخاصة بي ، والبعض الآخر لديه نفس بنية هذا واحد
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;
};
المحلول
إذا كان لديك أنواع استثناءات متعددة ، وافتراض أن هناك تسلسل هرمي للاستثناءات (وكلها مستمدة علنًا من بعض الفئة الفرعية std::exception
،) ابدأ من الأكثر تحديداً وتواصل أكثر عمومية:
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
}
من ناحية أخرى ، إذا كنت مهتمًا برسالة الخطأ فقط - throw
نفس الاستثناء ، قل std::runtime_error
مع رسائل مختلفة ، ثم catch
الذي - التي:
try
{
// code throws some subclass of std::exception
}
catch ( const std::exception& e )
{
std::cerr << "ERROR: " << e.what() << std::endl;
}
تذكر أيضًا - رمي بالقيمة ، و catch بواسطة [const] المرجع.
نصائح أخرى
يجب عليك إنشاء فئة استثناء قاعدة وجعل جميع الاستثناءات المحددة الخاصة بك مستمدة منه:
class BaseException { };
class HourOutOfRangeException : public BaseException { };
class MinuteOutOfRangeException : public BaseException { };
يمكنك بعد ذلك التقاطهم جميعًا في كتلة قبضة واحدة:
catch (const BaseException& e) { }
إذا كنت تريد أن تكون قادرًا على الاتصال GetMessage
, ، ستحتاج إلى:
- ضع هذا المنطق في
BaseException
, ، أو - صنع
GetMessage
وظيفة عضو افتراضي فيBaseException
وتجاوزه في كل فئات الاستثناء المشتقة.
قد تفكر أيضًا في استثناء استثناءاتك من أحد استثناءات المكتبة القياسية ، مثل std::runtime_error
واستخدم الاصطلاحي what()
وظيفة العضو بدلا من GetMessage()
.
اشتق كل استثناءاتك من فئة قاعدة مشتركة BaseException
التي لها طريقة افتراضية GetMessage()
.
ثم catch(const BaseException& e)
.
واجهت مشكلة مماثلة اليوم ، لكن اتضح أنني لم أكن بحاجة إلى حل لحل مشكلتي. بصراحة ، لم أستطع التفكير في حالات الاستخدام الحقيقية (التسجيل؟) ، ولم أجد استخدامًا كبيرًا له في الكود الخاص بي.
على أي حال ، هذا نهج مع قوائم النوع (يتطلب C ++ 11). أعتقد أن ميزة هذا النهج هو أنه لا توجد حاجة إلى الحصول على فئة قاعدة مشتركة للاستثناءات المخصصة (باستثناء استثناء STD :: ، ربما؟). بمعنى آخر ، ليس تدخليًا لتسلسلك الهرمي.
قد يكون هناك بعض الأخطاء الدقيقة التي لا أعرفها.
#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.
}
}
عندما لا تستطيع القوالب ، تنقذ وحدات الماكرو اليوم. الحل مأخوذ من تعزيز. يتلخص في 7 أسطر من التعليمات البرمجية.
/// @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
واجهت نفس المشكلة وهنا ما انتهى بي الأمر:
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;
}
في حالتي ، تعود الوظيفة عندما لا تحصل على استثناء. لذلك لا يتعين علي فعل أي شيء داخل المصيد ولكن يمكنني القيام بالعمل خارج المحاولة.
#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;
}`