【问题标题】:Catching a Python exception in C++在 C++ 中捕获 Python 异常
【发布时间】:2011-12-26 10:16:48
【问题描述】:

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

在服务器端:-

我有一个 C++ 类Test,该类使用SWIG 的director 特性在名为TestPython 的Python 中继承。 另外我在 C++ 中有一个异常类 MyException。

现在 TestPython 类的函数从 Python 代码中抛出 MyException()

我想使用 SWIG 在 C++ 代码中处理 Python 抛出的异常。

下面是代码sn-p:

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(interface) 文件中写什么来处理这个问题。

上面用Python写的TestException()是客户端调用的,所以如果服务器抛出异常我必须通知客户端。

【问题讨论】:

  • 您是否已经查看过有关此主题的 SWIG 文档? (swig.org/Doc2.0/SWIGDocumentation.html#Customization_exception)
  • 您可能希望更明显地表明您正在使用 SWIG 的导演功能 - 我第一次阅读这个问题时错过了它,并且在您的代码中根本没有任何暗示已经显示了。
  • @gecco - 该文档根本不适用于导演(即director:except)的 SWIG Python 代码引发的异常。

标签: c++ python exception swig


【解决方案1】:

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

它处理来自director 调用的错误,查看它是否是我们的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 否则将无法工作)我必须使用-py3 调用 SWIG。这反过来意味着我必须升级到 SWIG 2.0,因为我安装的 Python 3.2 副本已经从 SWIG 1.3.40 调用的 C-API 中删除了一些不推荐使用的函数。

【讨论】:

  • 如果我还有一个名为 class AnotherException 的异常类:public std::exception {}; 能否请您说明如何处理该异常?如何区分从 python 代码中抛出的异常以及将添加哪些代码来处理这两种类型的异常。提前感谢
  • @Gunjan49 - 我更新了示例以展示如何处理多种类型的异常。我认为这是最简单的方法,它使用 SWIG 类型系统来确定它是什么类型。如果你有一个多态类型层次结构,你可以在这里利用它。调用者无法区分异常来自哪里——这就是它的好处。 (如果您想弄清楚它是从哪里来的,这表明您对我做错了什么,但是您可以director:except 代码中的异常上设置一个标志以指示来源,如果您真的很想做)
猜你喜欢
  • 2011-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-10
相关资源
最近更新 更多