2009-05-11 43 views
15

Tôi đang phát triển một api C cho một số chức năng được viết bằng C++ và tôi muốn đảm bảo rằng không có ngoại lệ nào được truyền từ bất kỳ hàm C được xuất nào.Sử dụng lại mã trong xử lý ngoại lệ

Cách đơn giản để làm điều đó là đảm bảo mỗi chức năng xuất khẩu được chứa trong một:

try { 
    // Do the actual code 
} catch (...) { 
    return ERROR_UNHANDLED_EXCEPTION; 
} 

Hãy nói rằng tôi biết một ngoại lệ mà thường bị bỏ lỡ trong C++ là std :: bad_alloc và tôi muốn để đối xử với nó đặc biệt tôi muốn viết một cái gì đó như thế này thay vì:

try { 
    // Run the actual code 
} catch (std::bad_alloc& e) { 
    return ERROR_BAD_ALLOC; 
} catch (...) { 
    return ERROR_UNHANDLED_EXCEPTION; 
} 

có thể phân hủy này trong một số cách thông minh để tôi trên toàn cầu có thể điều trị một số lỗi khác nhau mà không cần thêm một lệnh catch mới cho xử lý ngoại lệ xung quanh mỗi chức năng xuất khẩu?

Tôi biết rằng điều này có thể giải quyết bằng cách sử dụng bộ tiền xử lý, nhưng trước khi đi xuống con đường đó, tôi chắc chắn rằng không có cách nào khác để làm điều đó.

Trả lời

27

Bạn có thể chỉ sử dụng một hàm điều khiển cho tất cả các trường hợp ngoại lệ có thể, và gọi nó là từ mỗi hoặc các chức năng thực hiện API của bạn, như sau:

int HandleException() 
{ 
    try 
    { 
     throw; 
    } 

    // TODO: add more types of exceptions 

    catch(std::bad_alloc &) 
    { 
     return ERROR_BAD_ALLOC; 
    } 
    catch(...) 
    { 
     return ERROR_UNHANDLED_EXCEPTION; 
    } 
} 

Và trong mỗi chức năng đã xuất:

try 
{ 
    ... 
} 
catch(...) 
{ 
    return HandleException(); 
} 
+1

+1: Ý tưởng hay :-) –

+0

Làm việc hoàn hảo. Cảm ơn! – Laserallan

+2

Trong mã thực không quên bắt ngoại lệ bằng cách tham khảo: bắt (std :: bad_alloc &) – Jem

0

Không bao giờ sử dụng catch(...), trừ khi bạn có kế hoạch trả lại nhiều hơn hoặc ít hơn ngay lập tức. Bạn chắc chắn sẽ mất bất kỳ thông tin lỗi nào mà bạn có thể có để giúp bạn tìm ra nguyên nhân gây ra lỗi.

Tôi thích kế hoạch thứ hai của bạn tốt hơn một chút - bắt được một bộ ngoại lệ được biết đến, lý tưởng là chúng là mã duy nhất mà mã của bạn sẽ ném và để phần còn lại thông qua - cho phép ứng dụng gặp sự cố. kể từ khi bạn đã gọi hành vi không rõ nó là tốt nhất để "sụp đổ có trách nhiệm".

+4

Trong trường hợp này, tôi nghĩ việc sử dụng catch (...) là điều đúng đắn cần làm.Trong C++ nó là difficcult, nếu không phải là không thể, để dự đoán tất cả các ngoại lệ có thể được ném, nhưng anh ta phải ngăn chặn chúng truyền bá mã C của mình. –

+0

Nếu bạn làm điều này, sau đó lên kế hoạch nhập một báo cáo lỗi xấu với ít hoặc không có thông tin về nguyên nhân gây ra sự cố và không có cách nào để nhận thêm –

+0

Vì vậy, bạn muốn chương trình bị lỗi vì một số thư viện tối nghĩa bạn phụ thuộc vào hai loại bỏ quyết định nó sẽ ném MyWeirdError? Tôi nghĩ rằng tôi muốn nhìn thấy "vấn đề không xác định với hoạt động XXX" trong nhật ký. –

1

gì về:

try{ 
    //Your code here 
} catch(std::exception e) 
{ 
    return translateExceptionToErrorCode(e); 
} catch(...) 
{ 
    return UNKNOWN_EXCEPTION_THROWN; 
} 
+2

Thông thường, bạn sẽ bắt gặp ngoại lệ bằng cách tham chiếu, để tránh sao chép không cần thiết –

+3

Chưa kể đến việc cắt. –

+0

Điểm rất tốt. Hãy coi mã của tôi là giả;) – PaulJWilliams

0

Sẽ là một điều đáng tiếc khi thông tin lỗi ở ngôn ngữ bo undary. Bạn thực sự nên cố gắng dịch tất cả các ngoại lệ thành mã lỗi có thể sử dụng từ C.

Cách bạn thực hiện điều đó thực sự phụ thuộc vào các lớp ngoại lệ của bạn. Nếu bạn kiểm soát phân cấp lớp ngoại lệ của mình, bạn có thể đảm bảo rằng mỗi lớp cung cấp bản dịch bằng cách sử dụng một phương thức ảo. Nếu không, bạn vẫn có thể thấy nó thực tế để sử dụng một hàm dịch và kiểm tra các loại ngoại lệ 'std :: exception' có nguồn gốc nó nhận được để dịch nó thành một mã lỗi, giống như Jem gợi ý (nhớ: ném ngoại lệ sẽ bị tổn thương hiệu suất anyway, do đó, đừng lo lắng về bản dịch được làm chậm).

+3

Dịch ngoại lệ sang mã lỗi có vẻ chính xác là những gì anh ta đang làm! –

1

Jem câu trả lời đơn giản hơn một chút so với giải pháp này. Nhưng có thể thay thế việc sử dụng macro tiền xử lý bằng việc sử dụng các mẫu. Một cái gì đó như thế này (bạn có thể tinh chỉnh thêm):

template <class T, void (T::*FUNC)()> 
class CatchWrapper 
{ 
public: 

    static void WrapCall(T* instance) 
    { 
     try 
     { 
      (instance->*FUNC)(); 
     } 
     catch (std::bad_alloc&) 
     { 
      // Do Something 1 
     } 
     catch (std::exception& e) 
     { 
      // Do Something 2 
     } 
     catch (...) 
     { 
      // Do Something 3 
     } 
    } 
}; 


class Foo 
{ 
public: 
    void SomeCall() 
    { 
     std::cout << "Do Something" << std::endl; 
    } 
}; 


int main(int argc, char* argv[]) 
{ 
    Foo i; 
    CatchWrapper<Foo, &Foo::SomeCall>::WrapCall(&i); 
    return 0; 
} 
+0

đó là những gì tôi đã mặc dù đầu tiên quá, (và thực sự những gì tôi đã sử dụng trong một số dự án của tôi). Nhưng anwser của Jem là sạch hơn từ quan điểm của tôi. Trong khi nó không làm phiền tôi, đôi khi các mẫu sử dụng làm phiền các lập trình viên khác ..:/ –

4

Đã có câu trả lời hay. Nhưng chỉ FYI, được gọi là thành ngữ 'ngoại lệ-điều phối', xem C++ FAQ 17.15. IMHO 17.16 cũng là một nội dung quan trọng.

+0

Cảm ơn, đây là một bài đọc tuyệt vời. – Laserallan

+0

Bây giờ là [17.15] [http://www.parashift.com/c%2B%2B-faq-lite/throw-without-an-object.html] – baruch

+0

@baruch: Cảm ơn, đã sửa thành url mới. – Abhay