2013-03-28 30 views
5

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.
+1

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). –

+0

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. –

+0

@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. –

Trả lời

2

Đã làm việc với BC++ Builder Tôi đã gặp phải cùng một vấn đề và macro dường như là giải pháp duy nhất vào thời điểm đó.

Giải pháp "sạch hơn" (hum ...) có thể là "thử hai lần": bên trong try-catch chuyển đổi ngoại lệ cũ của bạn thành lớp tiêu chuẩn và bên ngoài thực sự xử lý ngoại lệ.

Tôi không có mã trong tầm tay (nó được năm) nhưng về cơ bản nó nắm tới:

#define DTRY try { try 
#define DCATCH catch (Exception& e) { throw LegacyException(e); } } catch 

DTRY { 
    ... 
} 
DCATCH(std::exception& e) { 
    // handle the exception 
} 

Có, tôi biết đó là xấu xí, nhưng khi tôi làm việc với Borland tôi không tìm thấy bất cứ điều gì tốt hơn. Sự thật là, vào thời điểm đó Borland là một người không chuẩn mực và tôi không biết nó phát triển như thế nào, có lẽ bạn có thể làm tốt hơn ngày nay. Hy vọng điều này sẽ giúp anyway.

+0

Ngày nay không tốt hơn nhiều, IMHO, nhưng tôi phải sống với nó ngay bây giờ. Giả sử họ đang chuyển trình biên dịch sang một thứ dựa trên clang nào đó sẽ giúp ích rất nhiều - nhưng khi tôi kiểm tra một vài tháng trước, không có khung thời gian cho trình biên dịch clang 32 bit ... –

+0

@JamesJohnston Tôi cảm thấy nỗi đau của bạn, Borland năm cũng là một cơn ác mộng. ;) Chuyển sang GCC (và MinGW khi tôi phải làm cross-nền tảng) là sự cứu rỗi của tôi. – syam

Các vấn đề liên quan