Std को कैसे फेंकें :: चर संदेशों के साथ अपवाद?


121

यह एक उदाहरण है जो मैं अक्सर करता हूं जब मैं कुछ जानकारी को अपवाद में जोड़ना चाहता हूं:

std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());

क्या इसे करने का कोई अच्छा तरीका है?


10
मैं सोच रहा हूं कि आपने इस तरह से काम करने में कैसे कामयाब रहे - आरजी के std∷exceptionसाथ कोई कंस्ट्रक्टर नहीं है char*
हाय-एंजेल

2
मैं एक ही बात सोच रहा हूँ। शायद यह c ++ के लिए एक गैर-मानक एमएस एक्सटेंशन है? या शायद C ++ 14 में कुछ नया? वर्तमान दस्तावेज़ीकरण std :: अपवाद निर्माणकर्ता कोई तर्क नहीं लेता है।
क्रिस वॉर्थ

1
हाँ, लेकिन std::stringएक अंतर्निहित निर्माता है जो const char*...
ब्राइस एम। डेम्पसे

6
@Chris वार्थ यह एमएस की 'की पर्दे के पीछे कार्यान्वयन हिस्सा प्रतीत होता है std::exceptionरों बच्चे कक्षाओं', और के उनके संस्करणों द्वारा किया जाता है std::runtime_errorऔर std::logic_error। मानक द्वारा परिभाषित लोगों के अलावा, MSVS के संस्करण <exception>में दो और निर्माता भी शामिल हैं, एक लेने वाला (const char * const &)और दूसरा लेने वाला (const char * const &, int)। वे एक निजी चर सेट करने के लिए उपयोग किए जाते हैं const char * _Mywhat; यदि _Mywhat != nullptr, तो what()इसे वापस करने के लिए चूक। कोड जो इस पर निर्भर करता है वह शायद पोर्टेबल नहीं है।
जस्टिन टाइम -

जवाबों:


49

यहाँ मेरा समाधान है:

#include <stdexcept>
#include <sstream>

class Formatter
{
public:
    Formatter() {}
    ~Formatter() {}

    template <typename Type>
    Formatter & operator << (const Type & value)
    {
        stream_ << value;
        return *this;
    }

    std::string str() const         { return stream_.str(); }
    operator std::string () const   { return stream_.str(); }

    enum ConvertToString 
    {
        to_str
    };
    std::string operator >> (ConvertToString) { return stream_.str(); }

private:
    std::stringstream stream_;

    Formatter(const Formatter &);
    Formatter & operator = (Formatter &);
};

उदाहरण:

throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData);   // implicitly cast to std::string
throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData >> Formatter::to_str);    // explicitly cast to std::string

1
omg मैं देख रहा हूँ कि ऐसा कुछ कैसे करना है। लेकिन शायद ऑपरेटर को बदल
देगा-

3
इस और एक std :: stringstream के बीच क्या अंतर है? यह एक स्ट्रिंगस्ट्रीम सम्‍मिलित करता है, लेकिन इसमें (जहां तक ​​मैं बता सकता हूं), कोई अतिरिक्‍त कार्यक्षमता नहीं है।
मैट

2
आम तौर पर, यह 100% सुरक्षित तरीका नहीं है। std :: stringstream विधियाँ एक अपवाद को फेंक सकती हैं। समस्या यहाँ काफी अच्छी बताई गई है: boost.org/community/error_handling.html
Arthur P. Golubev

1
@ ArthurP.Golubev लेकिन इस मामले में, एक फ़ॉर्मेटर () उदाहरण भी पर्दे के पीछे एक स्ट्रिंगस्ट्रीम बनाता है, जो फिर से, एक अपवाद फेंक सकता है। तो क्या अंतर है?
ज़ुजु कॉर्नेलिउ

केवल जोड़ा कार्यक्षमता ConvertToString चाल और स्ट्रिंग के लिए स्पष्ट डाली है, जो वैसे भी अच्छा है। ;)
ज़ुजु कोर्नेलियू

178

मानक अपवादों का निर्माण निम्न में से किया जा सकता है std::string:

#include <stdexcept>

char const * configfile = "hardcode.cfg";
std::string const anotherfile = get_file();

throw std::runtime_error(std::string("Failed: ") + configfile);
throw std::runtime_error("Error: " + anotherfile);

ध्यान दें कि आधार वर्ग std::exceptionकर सकते हैं नहीं इस प्रकार का निर्माण किया; आपको कंक्रीट, व्युत्पन्न वर्गों में से एक का उपयोग करना होगा।


27

वहाँ इस तरह के रूप में विभिन्न अपवाद हैं runtime_error, range_error, overflow_error, logic_error, आदि .. आप अपने निर्माता में स्ट्रिंग पारित करने के लिए की जरूरत है, और आप जोड़ सकते हैं, जो आप अपने संदेश देना चाहते हैं। यह सिर्फ एक स्ट्रिंग ऑपरेशन है।

std::string errorMessage = std::string("Error: on file ")+fileName;
throw std::runtime_error(errorMessage);

आप boost::formatइस तरह का भी उपयोग कर सकते हैं :

throw std::runtime_error(boost::format("Error processing file %1") % fileName);

बढ़ावा :: प्रारूप संस्करण ऊपर एक स्पष्ट रूपांतरण के बिना संकलित नहीं होगा, यानी: runtime_error ((बढ़ावा :: प्रारूप ("पाठ% 1"% 2) .str ())। C ++ 20 एक std :: प्रारूप प्रस्तुत करता है जो समान कार्यक्षमता प्रदान करेगा।
दिगिरात

17

निम्न वर्ग काफी काम आ सकता है:

struct Error : std::exception
{
    char text[1000];

    Error(char const* fmt, ...) __attribute__((format(printf,2,3))) {
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(text, sizeof text, fmt, ap);
        va_end(ap);
    }

    char const* what() const throw() { return text; }
};

उपयोग उदाहरण:

throw Error("Could not load config file '%s'", configfile.c_str());

4
खराब प्रैक्टिस IMO, कुछ इस तरह का उपयोग क्यों करें जब पहले से ही एक मानक पुस्तकालय है जो अनुकूलन के लिए बनाया गया है?
जीन-मैरी कॉमपेट

3
throw std::runtime_error(sprintf("Could not load config file '%s'", configfile.c_str()))
जीन-मैरी कॉमप्स

4
throw std::runtime_error("Could not load config file " + configfile);( std::stringयदि आवश्यक हो तो एक या अन्य तर्क परिवर्तित करना)।
माइक सेमोर

9
@MikeSeymour हाँ, लेकिन अगर आपको किसी सटीक, आदि के साथ मध्य और प्रारूप संख्याओं में तार लगाने की आवश्यकता होती है, तो यह बदसूरत हो जाता है .. स्पष्टता के मामले में एक अच्छे पुराने प्रारूप स्ट्रिंग को हरा देना मुश्किल है।
मैक्सिम इगोरुस्किन

2
@MikeSeymour मैं सहमत हो सकता हूं कि मैंने जो कोड पोस्ट किया है, वह अपने समय से आगे हो सकता है। printfC ++ 11 में आस- पास के प्रकार और मित्र आसन्न हैं। फिक्स्ड साइज बफर एक आशीर्वाद और अभिशाप दोनों है: यह कम संसाधन स्थितियों में विफल नहीं होता है, लेकिन संदेश को काट सकता है। मैं एक त्रुटि संदेश को फिर से विफल करने के लिए एक बेहतर विकल्प को छोटा करने पर विचार करता हूं। साथ ही, कई अलग-अलग भाषाओं द्वारा प्रारूप के तार की सुविधा सिद्ध की गई है। लेकिन आप सही हैं, यह काफी हद तक स्वाद का मामला है।
मैक्सिम एगोरुस्किन

11

यदि C ++ 14 ( operator ""s) स्ट्रिंग स्ट्रिंग संचालक का उपयोग करें

using namespace std::string_literals;

throw std::exception("Could not load config file '"s + configfile + "'"s);

या C ++ 11 में अपना स्वयं का परिभाषित करें। उदाहरण के लिए

std::string operator ""_s(const char * str, std::size_t len) {
    return std::string(str, str + len);
}

आपका फेंक स्टेटमेंट फिर ऐसा दिखेगा

throw std::exception("Could not load config file '"_s + configfile + "'"_s);

जो अच्छा और साफ दिखता है।


2
मैं यह त्रुटि आई c ++ \ 7.3.0 \ बिट्स \ exception.h | 63 | ध्यान दें: 'के लिए std :: अपवाद कॉल :: अपवाद (std :: __ cxx11 :: basic_string <चार>) के लिए कोई मिलता-जुलता समारोह
हसीब मीर

@ श्रीवर्धन द्वारा वर्णित व्यवहार को एसटीडी लाइब्रेरी में परिभाषित नहीं किया गया है, हालांकि MSVC ++ इसे संकलित करेगा।
जॉचेन

0

वास्तव में एक अच्छा तरीका अपवादों के लिए एक वर्ग (या कक्षाएं) बना रहा होगा।

कुछ इस तरह:

class ConfigurationError : public std::exception {
public:
    ConfigurationError();
};

class ConfigurationLoadError : public ConfigurationError {
public:
    ConfigurationLoadError(std::string & filename);
};

कारण यह है कि अपवाद केवल एक स्ट्रिंग को स्थानांतरित करने की तुलना में बहुत अधिक बेहतर हैं। त्रुटियों के लिए अलग-अलग कक्षाएं प्रदान करते हुए, आप डेवलपर्स को एक पत्राचार तरीके से एक विशेष त्रुटि को संभालने का मौका देते हैं (न कि केवल एक त्रुटि संदेश प्रदर्शित करें)। यदि आप एक पदानुक्रम का उपयोग करते हैं तो आपके अपवाद को पकड़ने वाले लोग उतने ही विशिष्ट हो सकते हैं जितने की उन्हें आवश्यकता होती है।

a) किसी विशिष्ट कारण को जानने की आवश्यकता हो सकती है

} catch (const ConfigurationLoadError & ex) {
// ...
} catch (const ConfigurationError & ex) {

a) दूसरा विवरण नहीं जानना चाहता है

} catch (const std::exception & ex) {

आप इस विषय पर कुछ जानकारी https://books.google.ru/books?id=6tjfmnKhT24C पर पा सकते हैं अध्याय 17

इसके अलावा, आप एक कस्टम संदेश भी प्रदान कर सकते हैं, लेकिन सावधान रहना - इसके साथ एक संदेश लिखने के लिए सुरक्षित नहीं है या तो std::stringया std::stringstreamया किसी अन्य तरह से जो एक अपवाद पैदा कर सकता है

आम तौर पर, कोई फर्क नहीं पड़ता है कि आप मेमोरी को आवंटित करते हैं (सी ++ तरीके से तार के साथ काम) अपवाद के निर्माता में या फेंकने से पहले - std::bad_allocअपवाद को उस चीज से पहले फेंक दिया जा सकता है जिसे आप वास्तव में चाहते हैं।

तो, स्टैक पर आवंटित बफर (जैसे मैक्सिम के उत्तर में) एक सुरक्षित तरीका है।

इसे http://www.boost.org/community/error_handling.html पर बहुत अच्छी तरह से समझाया गया है

तो, एक अच्छा प्रकार का अपवाद अपवाद होगा और स्वरूपित स्ट्रिंग (कम से कम जब फेंकना) की रचना से बचना होगा।


0

एक समान समस्या में भाग, उस में मेरे कस्टम अपवाद के लिए कस्टम त्रुटि संदेश बनाने बदसूरत कोड बनाते हैं। यह मेरा समाधान था:

class MyRunTimeException: public std::runtime_error
{
public:
      MyRunTimeException(const std::string &filename):std::runtime_error(GetMessage(filename)) {}
private:
      static std::string GetMessage(const std::string &filename)
     {
           // Do your message formatting here. 
           // The benefit of returning std::string, is that the compiler will make sure the buffer is good for the length of the constructor call
           // You can use a local std::ostringstream here, and return os.str()
           // Without worrying that the memory is out of scope. It'll get copied
           // You also can create multiple GetMessage functions that take all sorts of objects and add multiple constructors for your exception
     }
}

यह संदेश बनाने के लिए तर्क को अलग करता है। मैंने मूल रूप से क्या () को ओवरराइड करने के बारे में सोचा था, लेकिन फिर आपको अपने संदेश को कहीं पर कब्जा करना होगा। std :: runtime_error में पहले से ही एक आंतरिक बफर है।


0

शायद यह?

throw std::runtime_error(
    (std::ostringstream()
        << "Could not load config file '"
        << configfile
        << "'"
    ).str()
);

यह एक अस्थायी अस्थि-पंजर बनाता है, << संचालकों को आवश्यकतानुसार कॉल करता है और फिर आप उसे राउंड ब्रैकेट में लपेटते हैं और मूल्यांकन किए गए परिणाम (जो एक अस्थि-पंजर है) पर tostr () फ़ंक्शन को कॉल करते हैं। of runtime_error।

नोट: ओस्ट्रिंगस्ट्रीम और स्ट्रिंग आर-वैल्यू टेम्परेरी हैं और इसलिए इस लाइन के समाप्त होने के बाद स्कोप से बाहर निकल जाएंगे। आपके अपवाद ऑब्जेक्ट के निर्माता को प्रतिलिपि या (बेहतर) चाल शब्दार्थ का उपयोग करके इनपुट स्ट्रिंग लेना होगा।

अतिरिक्त: मैं जरूरी नहीं कि इस दृष्टिकोण को "सर्वोत्तम अभ्यास" मानता हूं, लेकिन यह काम करता है और चुटकी में इस्तेमाल किया जा सकता है। सबसे बड़ी समस्याओं में से एक यह है कि इस पद्धति में ढेर आवंटन की आवश्यकता है और इसलिए ऑपरेटर << फेंक सकता है। आप शायद ऐसा नहीं चाहते हैं; हालाँकि, अगर आपका राज्य उस स्थिति में आ गया है तो आपके पास चिंता करने के लिए और अधिक मुद्दे हैं!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.