Saya sedang mengode perpustakaan kecil dan saya mengalami masalah dengan merancang penanganan pengecualian. Saya harus mengatakan bahwa saya (masih) bingung dengan fitur bahasa C ++ ini dan saya mencoba membaca sebanyak mungkin tentang masalah ini untuk memahami apa yang harus saya lakukan untuk bekerja dengan benar dengan kelas pengecualian.
Saya memutuskan untuk menggunakan system_error
jenis pendekatan yang mengambil inspirasi dari implementasi future_error
kelas STL .
Saya memiliki enumerasi yang berisi kode kesalahan:
enum class my_errc : int
{
error_x = 100,
error_z = 101,
error_y = 102
};
dan satu kelas pengecualian (didukung oleh error_category
jenis struktur dan segala sesuatu yang dibutuhkan oleh system_error
model):
// error category implementation
class my_error_category_impl : public std::error_category
{
const char* name () const noexcept override
{
return "my_lib";
}
std::string message (int ec) const override
{
std::string msg;
switch (my_errc(ec))
{
case my_errc::error_x:
msg = "Failed 1.";
break;
case my_errc::error_z:
msg = "Failed 2.";
break;
case my_errc::error_y:
msg = "Failed 3.";
break;
default:
msg = "unknown.";
}
return msg;
}
std::error_condition default_error_condition (int ec) const noexcept override
{
return std::error_condition(ec, *this);
}
};
// unique instance of the error category
struct my_category
{
static const std::error_category& instance () noexcept
{
static my_error_category_impl category;
return category;
}
};
// overload for error code creation
inline std::error_code make_error_code (my_errc ec) noexcept
{
return std::error_code(static_cast<int>(ec), my_category::instance());
}
// overload for error condition creation
inline std::error_condition make_error_condition (my_errc ec) noexcept
{
return std::error_condition(static_cast<int>(ec), my_category::instance());
}
/**
* Exception type thrown by the lib.
*/
class my_error : public virtual std::runtime_error
{
public:
explicit my_error (my_errc ec) noexcept :
std::runtime_error("my_namespace ")
, internal_code(make_error_code(ec))
{ }
const char* what () const noexcept override
{
return internal_code.message().c_str();
}
std::error_code code () const noexcept
{
return internal_code;
}
private:
std::error_code internal_code;
};
// specialization for error code enumerations
// must be done in the std namespace
namespace std
{
template <>
struct is_error_code_enum<my_errc> : public true_type { };
}
Saya hanya memiliki sejumlah kecil situasi di mana saya melempar pengecualian yang diilustrasikan oleh enumerasi kode kesalahan.
Di atas tidak cocok dengan salah satu pengulas saya. Dia berpendapat bahwa saya seharusnya membuat hierarki kelas pengecualian dengan basis kelas yang berasal dari std::runtime_error
karena memiliki kode kesalahan tertanam dalam kondisi mencampur hal-hal - pengecualian dan kode kesalahan - dan itu akan lebih membosankan untuk berurusan dengan suatu titik penanganan; hierarki pengecualian juga akan memudahkan kustomisasi pesan kesalahan.
Salah satu argumen saya adalah bahwa saya ingin tetap sederhana, bahwa perpustakaan saya tidak perlu membuang beberapa jenis pengecualian dan kustomisasi juga mudah dalam hal ini seperti yang ditangani secara otomatis - yang error_code
telah menjadi error_category
terkait dengan itu yang menerjemahkan kode ke pesan kesalahan yang tepat.
Saya harus mengatakan bahwa saya tidak mempertahankan pilihan saya dengan baik, bukti fakta bahwa saya masih memiliki beberapa kesalahpahaman tentang pengecualian C ++.
Saya ingin tahu apakah desain saya masuk akal. Apa kelebihan metode lain dari yang saya pilih karena harus saya akui juga gagal melihatnya? Apa yang bisa saya lakukan untuk meningkatkan?
sumber
Jawaban:
Saya pikir kolega Anda benar: Anda mendesain kasus pengecualian Anda berdasarkan seberapa sederhana penerapannya dalam hierarki, bukan berdasarkan kebutuhan penanganan-pengecualian dari kode klien.
Dengan satu jenis pengecualian dan penghitungan untuk kondisi kesalahan (solusi Anda), jika kode klien perlu menangani kasus kesalahan tunggal (misalnya,
my_errc::error_x
) mereka harus menulis kode seperti ini:Dengan beberapa tipe pengecualian (memiliki basis umum untuk seluruh hierarki), Anda dapat menulis:
di mana kelas pengecualian terlihat seperti ini:
Saat menulis perpustakaan, fokusnya harus pada kemudahan penggunaannya, bukan kemudahan implementasi internal.
Anda hanya harus mengkompromikan kemudahan penggunaan (bagaimana kode klien akan terlihat) ketika upaya melakukannya dengan benar di perpustakaan, adalah hal yang terlarang.
sumber
Saya setuju dengan pengulas Anda dan dengan @utnapistim. Anda dapat menggunakan
system_error
pendekatan ketika Anda menerapkan hal-hal lintas platform ketika beberapa kesalahan memerlukan penanganan khusus. Tetapi bahkan dalam kasus ini, itu bukan solusi yang baik, tetapi solusi yang kurang jahat.Satu hal lagi. Saat membuat hierarki pengecualian, jangan membuatnya terlalu dalam. Buat hanya kelas pengecualian itu, yang bisa diproses oleh klien. Dalam kebanyakan kasus, saya hanya menggunakan
std::runtime_error
danstd::logic_error
. Saya melemparstd::runtime_error
ketika terjadi kesalahan dan dan saya tidak dapat melakukan apa-apa (pengguna mengeluarkan perangkat dari komputer, ia lupa aplikasi itu masih berjalan), danstd::logic_error
ketika logika program rusak (Pengguna mencoba untuk menghapus catatan dari database yang tidak ada, tetapi sebelum menghapus operasi ia dapat memeriksanya, jadi dia mendapatkan kesalahan logika).Dan sebagai pengembang perpustakaan, pikirkan kebutuhan pengguna Anda. Cobalah untuk menggunakannya sendiri dan pikirkan, apakah itu nyaman untuk Anda. Daripada Anda dapat menjelaskan posisi Anda kepada pengulas Anda dengan contoh-contoh kode.
sumber