2012-05-21 37 views
6

Tôi gặp một số sự cố khi xử lý các ngoại lệ C++ tùy chỉnh khi gọi từ Cython. Tình huống của tôi là như sau: Tôi có một thư viện sử dụng CustomLibraryException cho tất cả các trường hợp ngoại lệ. Những gì tôi muốn về cơ bản là nhận được thông báo lỗi và đưa ra một lỗi Python với nó.Xử lý các ngoại lệ C++ tùy chỉnh trong Cython

user guide có một số gợi ý nhưng có một chút không xác định. Khả năng thứ nhất là để làm:

CDEF int bar() trừ + ValueError

này chuyển đổi CustomLibraryException đến một ValueError, nhưng mất thông báo lỗi.

Khả năng khác là chuyển đổi một cách rõ ràng các lỗi sử dụng

cdef int raise_py_error() 
cdef int something_dangerous() except +raise_py_error 

tôi không thực sự understant giải pháp này. Tôi hiểu rằng raise_py_error phải là một hàm C++ mà bằng cách nào đó xử lý lỗi. Tôi không chắc chắn làm thế nào để xử lý nó mặc dù. Hàm không nhận được đối số và được gọi bên trong khối catch trong C++.

Nếu có ai có ví dụ làm việc về xử lý ngoại lệ C++ trong Cython, điều đó sẽ giúp ích rất nhiều.

Trả lời

2

Nếu CustomLibraryException có nguồn gốc từ std::runtime_error (như một ngoại lệ C++ hoạt động tốt), thì hành vi bạn đang thấy là lỗi trong Cython.

Nếu không, thì điều dễ nhất để làm là để quấn C++ chức năng bạn đang gọi điện thoại trong một hàm C++ helper biên dịch ngoại lệ:

double foo(char const *, Bla const &); // this is the one we're wrapping 

double foo_that_throws_runtime_error(char const *str, Bla const &blaref) 
{ 
    try { 
     return foo(str, blaref); 
    } catch (CustomLibraryException const &e) { 
     throw std::runtime_error(e.get_the_message()); 
    } 
} 

này sẽ gây ra một RuntimeError được lớn lên ở phía Python. Hoặc, throw an std::invalid_argument đến raise a ValueError, v.v. (xem bảng trong trang bạn đã liên kết đến).

+0

Ngoại lệ không xuất phát từ '' std :: runtime'' như bạn mong đợi. Cảm ơn sự giúp đỡ :) Điều đó không làm cho nó tốt hơn nhiều, mặc dù. Các hàm nâng lỗi là các hàm thành viên và tôi không muốn thay đổi mã của chúng. Đây là về [gco wrappers] của tôi (http://peekaboo-vision.blogspot.de/2012/05/graphcuts-for-python-pygco.html) và giấy phép không cho phép tôi phân phối lại: -/ –

+1

@ AndreasMueller: các hàm thành viên có thể được bao bọc một cách dễ dàng trong các hàm freestanding; chỉ cần truyền đối tượng mà chúng sẽ hoạt động như đối số đầu tiên: 'void wrapper (Obj & o, int ham) {return o.wrapped (ham); } ' –

+1

Vâng tôi biết, tôi chỉ lười biếng để làm điều đó được nêu ra;) Btw, bạn có biết bạn là tấm poster thứ 100 trên SO? –

3

Giá trị mặc định C++ xử lý ngoại lệ trong Cython nên minh họa chính xác làm thế nào để thực hiện những gì bạn đang cố gắng để làm:

static void __Pyx_CppExn2PyErr() { 
    // Catch a handful of different errors here and turn them into the 
    // equivalent Python errors. 
    try { 
    if (PyErr_Occurred()) 
     ; // let the latest Python exn pass through and ignore the current one 
    else 
     throw; 
    } catch (const std::bad_alloc& exn) { 
    PyErr_SetString(PyExc_MemoryError, exn.what()); 
    } catch (const std::bad_cast& exn) { 
    PyErr_SetString(PyExc_TypeError, exn.what()); 
    } catch (const std::domain_error& exn) { 
    PyErr_SetString(PyExc_ValueError, exn.what()); 
    } catch (const std::invalid_argument& exn) { 
    PyErr_SetString(PyExc_ValueError, exn.what()); 
    } catch (const std::ios_base::failure& exn) { 
    // Unfortunately, in standard C++ we have no way of distinguishing EOF 
    // from other errors here; be careful with the exception mask 
    PyErr_SetString(PyExc_IOError, exn.what()); 
    } catch (const std::out_of_range& exn) { 
    // Change out_of_range to IndexError 
    PyErr_SetString(PyExc_IndexError, exn.what()); 
    } catch (const std::overflow_error& exn) { 
    PyErr_SetString(PyExc_OverflowError, exn.what()); 
    } catch (const std::range_error& exn) { 
    PyErr_SetString(PyExc_ArithmeticError, exn.what()); 
    } catch (const std::underflow_error& exn) { 
    PyErr_SetString(PyExc_ArithmeticError, exn.what()); 
    } catch (const std::exception& exn) { 
    PyErr_SetString(PyExc_RuntimeError, exn.what()); 
    } 
    catch (...) 
    { 
    PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); 
    } 
} 

Vì vậy, bạn có thể #define __Pyx_CppExn2PyErr your_custom_exn_handler trong một tập tin .h bao gồm để ghi đè lên các hành vi chung chung, hoặc sử dụng một trình xử lý tùy chỉnh một lần là

cdef extern from "...": 
    void your_exn_throwing_fcn() except +your_custom_exn_handler 
7

Đồng ý từ ngữ trong trang tài liệu để lại nội dung mong muốn. Trong khi "Cython không thể ném ngoại lệ C++", đây là lệnh raise_py_error thực hiện những gì chúng tôi muốn.

Đầu tiên, xác định các lớp tùy chỉnh ngoại lệ trong cython và thực hiện một xử lý nó bằng cách sử dụng "công cộng" từ khóa

from cpython.ref cimport PyObject 

class JMapError(RuntimeError): 
    pass 

cdef public PyObject* jmaperror = <PyObject*>JMapError 

Sau đó viết các xử lý ngoại lệ (các tài liệu không phải là siêu rõ ràng này phải được viết bằng C++ và nhập khẩu):

#include "Python.h" 
#include "jmap/cy_utils.H" 
#include "jmap/errors.H" 
#include <exception> 
#include <string> 

using namespace std; 

extern PyObject *jmaperror; 

void raise_py_error() 
{ 
    try { 
    throw; 
    } catch (JMapError& e) { 
    string msg = ::to_string(e.code()) +" "+ e.what(); 
    PyErr_SetString(jmaperror, msg.c_str()); 
    } catch (const std::exception& e) { 
    PyErr_SetString(PyExc_RuntimeError, e.what()); 
    } 
} 

Cuối cùng, mang handler vào cython với một khối extern, và sử dụng nó:

cdef extern from "jmap/cy_utils.H": 
    cdef void raise_py_error() 

void _connect "connect"() except +raise_py_error 

Xong. Bây giờ tôi thấy ngoại lệ mới, được xây dựng với mã lỗi như dự định:

JMapError: 520 timed connect failed: Connection refused 
+0

Câu trả lời liên tục. Bạn có biết nếu nó có thể làm cho Cython biên dịch rằng tập tin c + + trực tiếp hoặc tôi cần phải tự thực hiện một lib sau đó liên kết với nó? Tôi không thể tìm thấy bất kỳ tài liệu nào về việc này ... –

+0

Tôi đã phát hiện ra. Dưới đây là một ví dụ tối thiểu về xử lý lỗi trong khi vẫn bảo vệ thư khỏi bị lừa dối tùy chỉnh: https://github.com/SintefRaufossManufacturing/python-hcn/blob/master/hcn/cy_handler.cpp –

+1

Bạn có thể giải thích chính xác (tiện ích mở rộng tệp nào, nhập khẩu cần thiết) bạn sử dụng để khai báo lớp cython và đưa nó vào một 'PyObject'? (codeblock đầu tiên) PyObject không được định nghĩa mặc định và nhập nó từ '" Python.h "' làm cho cython phàn nàn rằng các đối tượng Python không thể được đưa vào các kiểu nguyên thủy ... – Silmathoron

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