2011-11-10 75 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