2011-11-10 159 views
3

我正在開發一個服務器 - 客戶端應用程序,其中客戶端調用服務器的API,該API爲用戶輸入提供Python接口。這意味着客戶端接口和服務器接口是用Python編寫的,而套接字代碼是用C++編寫的。在C++中捕獲Python異常

在服務器端: -

我有一個類,Test,C++和這個類是繼承使用Python中的SWIG主任特徵命名TestPython。 另外我在C++中有一個異常類MyException。

現在TestPython類的一個函數從Python代碼中拋出MyException()

我想處理在C++代碼中使用SWIG從Python拋出的異常。

下面的代碼片段:

C++代碼 -

class MyException 
{ 
    public: 
    string errMsg; 
    MyException(); 
    MyException(string); 
    ~MyException(); 
}; 

class Test 
{ 
    int value; 
    public: 
     void TestException(int val); 
     Test(int); 
}; 

Python代碼 -

class TestPython(Test): 
    def __init__(self): 
    Test.__init__(self) 

    def TestException(self,val): 
    if val > 20: 
     throw MyException("MyException : Value Exceeded !!!") 
    else:  
     print "Value passed = ",val 

現在,如果TestException()函數被調用,它應該拋出MyException。我想在我的C++代碼中處理這個MyException()異常。

所以任何人都可以建議我如何做到這一點,我的意思是我應該在我的* .i(接口)文件中編寫來處理這個問題。

以Python編寫的上述TestException()由客戶端調用,所以如果服務器拋出任何異常,我必須通知客戶端。

+0

你有沒有看過關於這個主題的SWIG文檔? (http://www.swig.org/Doc2.0/SWIGDocumentation.html#Customization_exception) – gecco

+0

你可能想讓它更明顯一點,就是你使用了SWIG的導演功能 - 我第一次讀這個時就錯過了它問題,並且在你展示的代碼中根本沒有任何提示。 – Flexo

+0

@gecco - 該文檔不適用於由SWIG引發的異常Python代碼來自導演(即'director:except')。 – Flexo

回答

3

要做到這一點,你基本上需要編寫一個%feature("director:except"),它可以處理Python異常並將其重新拋出爲C++異常。這裏有一個小而完整的例子:

假設我們有我們希望包裹下面的頭文件:

#include <iostream> 
#include <exception> 

class MyException : public std::exception { 
}; 

class AnotherException : public std::exception { 
}; 

class Callback { 
public: 
     virtual ~Callback() { std::cout << "~Callback()" << std:: endl; } 
     virtual void run() { std::cout << "Callback::run()" << std::endl; } 
}; 

inline void call(Callback *callback) { if (callback) callback->run(); } 

而且在使用該Python代碼:

import example 

class PyCallback(example.Callback): 
    def __init__(self): 
     example.Callback.__init__(self) 
    def run(self): 
     print("PyCallback.run()") 
     raise example.MyException() 

callback = PyCallback() 
example.call(callback) 

我們可以定義如下SWIG接口文件:

%module(directors="1") example 
%{ 
#include "example.h" 
%} 

%include "std_string.i" 
%include "std_except.i" 
%include "pyabc.i" 

// Python requires that anything we raise inherits from this 
%pythonabc(MyException, Exception); 

%feature("director:except") { 
    PyObject *etype = $error; 
    if (etype != NULL) { 
     PyObject *obj, *trace; 
     PyErr_Fetch(&etype, &obj, &trace); 
     Py_DecRef(etype); 
     Py_DecRef(trace); 
     // Not too sure if I need to call Py_DecRef for obj 

     void *ptr; 
     int res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_MyException, 0); 
     if (SWIG_IsOK(res) && ptr) { 
     MyException *e = reinterpret_cast< MyException * >(ptr); 
     // Throw by pointer (Yucky!) 
     throw e; 
     } 

     res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_AnotherException, 0); 
     if (SWIG_IsOK(res) && ptr) { 
     AnotherException *e = reinterpret_cast< AnotherException * >(ptr); 
     throw e; 
     } 

     throw Swig::DirectorMethodException(); 
    } 
} 

%feature("director") Callback; 
%include "example.h" 

哪個處理導演調用的錯誤,看起來t o查看它是否是我們的MyException實例之一,然後重新拋出指針(如果是的話)。如果您有多種類型的異常被拋出,那麼您可能需要使用PyErr_ExceptionMatches來確定它是第一種類型。

我們可以使用也扔通過值或引用:

// Throw by value (after a copy!) 
    MyException temp = *e; 
    if (SWIG_IsNewObj(res)) 
    delete e; 
    throw temp; 

代替,但要注意,如果你在Python扔了子類的MyException這將落在object slicing problem的犯規。

我不太確定代碼是否100%正確 - 特別是我認爲引用計數是正確的,但我可能是錯的。

注意:爲了使這個例子工作(%pythonabc將無法​​正常工作),我不得不打電話給SWIG -py3。這反過來意味着我必須升級到SWIG 2.0,因爲我已安裝的Python 3.2副本已從SWIG 1.3.40調用的C-API中刪除了一些棄用的函數。

+0

如果我有一個名爲class AnotherException的異常類,public std :: exception {};我如何區分哪些異常是從python代碼拋出的,哪些代碼將被添加來處理這兩種類型的異常。 Thanxx提前 – Gunjan49

+0

@ Gunjan49 - 我更新了這個例子來展示你如何處理多種類型的異常。我認爲這是最簡單的方法,它使用SWIG類型系統來確定它是什麼類型。如果你有一個多態的類型層次結構,你可以在這裏利用它。調用者無法區分異常來自哪裏 - 這是它的好處。 (如果你想知道它是從哪裏來的,這表明你做錯了我,但是你可以在'director:except'代碼的異常處設置一個標誌,以表示你真的想要做到這一點) – Flexo