Pertanyaan Bagaimana cara membuang std :: exception dengan pesan variabel?


Ini adalah contoh dari apa yang sering saya lakukan ketika saya ingin menambahkan beberapa informasi ke pengecualian:

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

Apakah ada cara yang lebih baik untuk melakukannya?


75
2017-09-04 10:48


asal


Jawaban:


Inilah solusi saya:

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 &);
};

Contoh:

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

28
2017-09-04 11:32



Pengecualian standar dapat dibangun dari a 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);

Perhatikan bahwa kelas dasar std::exception bisa tidak dibangun demikian; Anda harus menggunakan salah satu kelas turunan beton.


129
2017-09-04 10:50



Ada pengecualian yang berbeda seperti runtime_error, range_error, overflow_error, logic_error, dll. Anda harus meneruskan string ke konstruktornya, dan Anda dapat menggabungkan apa pun yang Anda inginkan ke pesan Anda. Itu hanya operasi string.

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

Anda juga bisa menggunakan boost::format seperti ini:

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

21
2017-09-04 10:53



Kelas berikut saya datang sangat berguna:

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; }
};

Contoh penggunaan:

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

13
2017-09-04 10:53



Gunakan string literal operator jika C ++ 14 (operator ""s)

using namespace std::string_literals;

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

atau tentukan sendiri jika di C ++ 11. Contohnya

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

Pernyataan lemparan Anda akan terlihat seperti ini

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

yang terlihat bagus dan bersih.


9
2017-09-09 09:05



Cara yang lebih baik akan menciptakan kelas (atau kelas) untuk pengecualian.

Sesuatu seperti:

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

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

Alasannya adalah bahwa pengecualian jauh lebih dari sekadar mentransfer string. Memberikan kelas yang berbeda untuk kesalahan, Anda memberi pengembang kesempatan untuk menangani kesalahan tertentu dengan cara yang berhubungan (tidak hanya menampilkan pesan kesalahan). Orang yang menangkap pengecualian Anda dapat menjadi spesifik yang mereka butuhkan jika Anda menggunakan hierarki.

a) Seseorang mungkin perlu mengetahui alasan spesifiknya

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

a) orang lain tidak ingin tahu detailnya

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

Anda dapat menemukan beberapa inspirasi tentang topik ini di https://books.google.ru/books?id=6tjfmnKhT24C Bab 9

Juga, Anda dapat memberikan pesan khusus juga, tetapi hati-hati - tidak aman menulis pesan dengan baik std::string atau std::stringstream atau cara lain yang dapat menyebabkan pengecualian.

Secara umum, tidak ada perbedaan apakah Anda mengalokasikan memori (bekerja dengan string dalam cara C ++) di konstruktor pengecualian atau sebelum melempar - std::bad_alloc pengecualian dapat dilemparkan sebelum yang benar-benar Anda inginkan.

Jadi, buffer yang dialokasikan pada stack (seperti dalam jawaban Maxim) adalah cara yang lebih aman.

Hal ini dijelaskan dengan sangat baik http://www.boost.org/community/error_handling.html

Jadi, cara yang lebih baik akan menjadi tipe khusus pengecualian dan menghindari penyusunan string yang diformat (setidaknya saat melempar).


1
2017-09-08 08:50