在 C++ 中捕获 Python 异常

2024-02-05 10:15:48 浏览数 (1)

在 C 中捕获 Python 异常的原理涉及到 Python C API 的使用和异常处理机制。下面简要介绍捕获 Python 异常的原理:Python C API 允许 C 代码与 Python 解释器进行交互,从而可以在 C 中调用 Python 函数、获取 Python 对象、捕获 Python 异常等操作。所以说能都捕获异常并做提示,针对后期代码优化有很大的帮助,下面就看看具体的解决方案吧。

1、问题背景

在开发一个服务器-客户端应用时,客户端会调用服务器的 API,该 API 提供了用于用户输入的 Python 接口。这意味着客户端接口和服务器接口是用 Python 编写的,而套接字代码是用 C 编写的。

在服务器端,我有一个 C 类的 Test,我们用 SWIG 的管理机制在 Python 中继承 Test,命名为 TestPython。我还定义一个 C 中的异常类 MyException。现在,TestPython 类的一个函数从 Python 代码中抛出了 MyException()。

我希望在 C 代码中使用 SWIG 来处理从 Python 中抛出的异常。因此,我想知道应该在 *.i(接口)文件中写什么来处理这种情况。

2、解决方案

为了实现这个功能,您需要编写一个 �ature("director:except"),它可以处理 Python 异常并将其重新抛出为 C 异常。以下是一个简单但完整的示例:

头文件 example.h

代码语言:javascript复制
#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 代码

代码语言:javascript复制
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 接口文件

代码语言:javascript复制
%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);
​
�ature("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();
    }
}
​
�ature("director") Callback;
%include "example.h"

代码会处理来自管理函数的错误,检查它是否是我们 MyException 实例之一,如果是,则重新抛出指针。如果您有多种类型的异常被抛出,那么您可能需要先使用 PyErr_ExceptionMatches 来计算出它的类型。

通过调用 SWIG 使用 -py3 参数,我们就可以让这个示例工作(否则 %pythonabc 不起作用)。这又意味着我们必须升级到 SWIG 2.0,因为我安装的 Python 3.2 从 C-API 中删除了一些 SWIG 1.3.40 调用的已弃用的函数。

在调用 Python 函数后,可以使用 PyErr_Occurred() 检查是否发生了异常,并使用 PyErr_Print() 打印异常信息。在实际应用中,你可能需要根据你的需求进行更详细的异常处理。

此外,要确保在 C 代码中正确处理 Python 的引用计数,避免内存泄漏,可以使用 Py_XDECREF 来递减引用计数。以上就是今天的全部内容,如果有更好的学习技巧或者需要解答的地方,记得评论区留言讨论。

0 人点赞