在c++中捕捉Python异常

Catching a Python exception in C++

本文关键字:Python 异常 c++      更新时间:2023-10-16

我正在开发一个服务器-客户端应用程序,其中客户端调用服务器的API,该API为用户输入提供Python接口。这意味着客户端接口和服务器接口是用Python编写的,而套接字代码是用c++编写的。

服务器端:-

我有一个类,Test,在c++中,这个类是在Python中继承的,名为TestPython,使用SWIG的导演功能。我也有一个异常类MyException在c++。

现在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()是由客户端调用的,所以如果服务器抛出任何异常,我必须通知客户端。

要做到这一点,你基本上需要编写一个%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"

处理一个错误从导演调用,看看它是否是我们的MyException实例之一,然后重新抛出指针,如果它是。如果你有多种类型的异常被抛出,那么你可能需要使用PyErr_ExceptionMatches来确定它是什么类型的。

也可以通过值或引用抛出:

  // Throw by value (after a copy!)
  MyException temp = *e;
  if (SWIG_IsNewObj(res)) 
    delete e;
  throw temp;
但请注意,如果你在Python中抛出MyException子类,这将与对象切片问题发生冲突。

我不太确定代码是否100%正确-特别是我认为引用计数是正确的,但我可能错了。

注意:为了使这个例子工作(%pythonabc不会工作否则),我不得不调用SWIG与-py3。这反过来意味着我必须升级到SWIG 2.0,因为我安装的Python 3.2副本已经从SWIG 1.3.40调用的C-API中删除了一些已弃用的函数。