Giả sử tôi có hai cấu trúc phân cấp thừa kế mà tôi đang xử lý trong C++. Một kế thừa từ std::exception
(phân cấp mới) và một thừa kế khác từ Exception
(lớp ngoại lệ cơ sở VCL Builder C++ cũ). Nếu tôi gọi mã có thể ném một trong hai loại ngoại lệ, tôi phải viết mã như thế này:Bạn có thể bọc một hệ thống phân cấp thừa kế ngoại lệ vào một hệ thống phân cấp thừa kế khác không? - hay, một cách sạch sẽ khác để giải quyết vấn đề này?
try {
// do stuff....
Function1();
Function2();
} catch (std::exception &ex) {
std::cout << "STL exception caught: " << ex.what() << std::endl;
} catch (Exception &ex) {
std::cout << "Legacy exception caught: " << ex.Message.c_str() << std::endl;
} catch (SomeOtherVendorLibraryException &ex) {
// etc.
}
Vấn đề là mỗi người gọi cần phải có tất cả những mệnh đề catch để cố gắng có được tất cả các loại cuối cùng của ngoại lệ, vì C++ không có một lớp cơ sở ngoại lệ đúng, được thực thi nào mà bạn có thể sử dụng như một phần tử bắt buộc (ví dụ: System.Exception
lớp trong C#). (catch (...)
là một công cụ không khởi động vì bạn không có cách nào để biết bạn bị bắt và một số ngoại lệ hệ thống nguy hiểm, như vi phạm truy cập, có thể bị đánh cắp tốt hơn.)
Tôi muốn để bao bọc các ngoại lệ "cũ" này thành một lớp trong phân cấp std::exception
. Khái niệm gói ngoại lệ của bên thứ ba này vào hệ thống ngoại lệ của riêng bạn không hoàn toàn chưa từng có. Ví dụ: Khuôn khổ .NET bao bọc các bộ lỗi rộng trong các hệ thống khác (ví dụ: COMException
). Lý tưởng nhất, tôi muốn thấy một cái gì đó như thế này:
class LegacyException : public std::runtime_error {
public:
// construct STL exception from legacy exception
LegacyException(const Exception &ex) : std::runtime_error(ex.Message.c_str()) {}
};
try {
// In reality, this throw will happen in some function we have no control over.
throw Exception("Throwing legacy exception!");
} catch (std::exception &ex) {
// Ideally, the compiler would use the LegacyException constructor
// to cast the thrown Exception to a LegacyException, which ultimately
// inherits from std::exception.
std::cout << ex.what() << std::endl;
}
Có thể hiểu được, ngoại lệ sẽ không bao giờ bị bắt - nó sẽ yêu cầu khá nhiều phép thuật từ trình biên dịch để bắt nó.
Có giải pháp nào có thể giống với giải pháp trên để bao gồm ngoại lệ cũ và đạt được các mục tiêu này không?
- Điều khoản "bắt" hoặc tương tự, do đó, logic xử lý ngoại lệ chung chỉ cần được viết một lần.
- Logic để chuyển đổi từ một loại ngoại lệ sang loại ngoại lệ khác phải được tập trung.
- Tránh macro nếu có thể.
- Không sử dụng hàm lambda.
Không có hàm lambda - là lý do bạn tránh các tính năng của C++ 11? (Tôi hỏi vì có các tính năng không phải lambda C++ 11 sẽ hữu ích). –
Ngoài ra, bạn đang nhắm mục tiêu trình biên dịch nào? Các trình biên dịch của Microsoft có một tính năng gây ra 'catch (...)' để bắt vi phạm truy cập, nhưng tính năng đó đã bị vô hiệu hóa theo mặc định từ VS2005. Nếu bạn đang loại bỏ 'catch (...)' trên những lý do đó, bạn sẽ giảm được nhiều lựa chọn hơn nữa. –
@JoeGauterin: C++ Hàm lambda không được hỗ trợ bởi một trong các trình biên dịch mà mã của tôi đang nhắm mục tiêu.Nếu không, tôi sẽ sử dụng chúng trong 2 giây để giải quyết vấn đề này nếu tôi có thể thuyết phục trình biên dịch BCB crufty chấp nhận chúng: catch (...), sau đó gọi hàm helper và truyền hàm lambda chấp nhận std :: exception to the người giúp đỡ. Helper rethrows exception, bắt các loại khác nhau và chuyển đổi khi cần thiết, sau đó gọi hàm lambda. Quá tệ tôi không thể sử dụng C++ 11. –